10bbfda8aSnia/* 20bbfda8aSnia * Misc function implementation 30bbfda8aSnia * 40bbfda8aSnia * These are things that either don't fit neatly into another category, 50bbfda8aSnia * or fit into a category too small to be worth making individual files 60bbfda8aSnia * for. 70bbfda8aSnia */ 80bbfda8aSnia 90bbfda8aSnia#include "ctwm.h" 100bbfda8aSnia 110bbfda8aSnia#include <stdlib.h> 120bbfda8aSnia 130bbfda8aSnia#include "animate.h" 14b18c2d1eSnia#include "ctwm_shutdown.h" 150bbfda8aSnia#include "functions.h" 160bbfda8aSnia#include "functions_defs.h" 170bbfda8aSnia#include "functions_internal.h" 180bbfda8aSnia#include "icons.h" 190bbfda8aSnia#include "otp.h" 200bbfda8aSnia#include "screen.h" 210bbfda8aSnia#ifdef SOUNDS 220bbfda8aSnia#include "sound.h" 230bbfda8aSnia#endif 240bbfda8aSnia#include "util.h" 250bbfda8aSnia#include "win_iconify.h" 26b18c2d1eSnia#ifdef WINBOX 270bbfda8aSnia#include "windowbox.h" 28b18c2d1eSnia#endif 290bbfda8aSnia#include "workspace_utils.h" 300bbfda8aSnia 310bbfda8aSnia#include "ext/repl_str.h" 320bbfda8aSnia 330bbfda8aSnia 340bbfda8aSnia 350bbfda8aSnia/* 360bbfda8aSnia * Animation-related 370bbfda8aSnia */ 380bbfda8aSniaDFHANDLER(startanimation) 390bbfda8aSnia{ 400bbfda8aSnia StartAnimation(); 410bbfda8aSnia} 420bbfda8aSnia 430bbfda8aSniaDFHANDLER(stopanimation) 440bbfda8aSnia{ 450bbfda8aSnia StopAnimation(); 460bbfda8aSnia} 470bbfda8aSnia 480bbfda8aSniaDFHANDLER(speedupanimation) 490bbfda8aSnia{ 500bbfda8aSnia ModifyAnimationSpeed(1); 510bbfda8aSnia} 520bbfda8aSnia 530bbfda8aSniaDFHANDLER(slowdownanimation) 540bbfda8aSnia{ 550bbfda8aSnia ModifyAnimationSpeed(-1); 560bbfda8aSnia} 570bbfda8aSnia 580bbfda8aSnia 590bbfda8aSnia 600bbfda8aSnia/* 610bbfda8aSnia * Menu-related 620bbfda8aSnia */ 630bbfda8aSniaDFHANDLER(menu) 640bbfda8aSnia{ 650bbfda8aSnia /* 660bbfda8aSnia * n.b.: The f.menu handler is all kinds of magic; it's actually 670bbfda8aSnia * completely unrelated to pulling up the menu. 680bbfda8aSnia * 690bbfda8aSnia * When a button/key binding invokes f.menu to open up a menu, that's 700bbfda8aSnia * actually handled in the KeyPress or ButtonPress handlers by 710bbfda8aSnia * calling do{_key,}_menu(). When we descend into a submenu, that's 720bbfda8aSnia * handled in KeyPress handler for keyboard navigation when we hit 730bbfda8aSnia * the Right arrow, or inside the 740bbfda8aSnia * event loop recapture in UpdateMenu() for mouse navigation when we 750bbfda8aSnia * move it to the right side of the menu entry. 760bbfda8aSnia * 770bbfda8aSnia * This handler is only used by "invoking" a menu item; releasing the 780bbfda8aSnia * mouse button on the left side without moving right to open out the 790bbfda8aSnia * submenu, or hitting the Enter key. All it does is immediately 800bbfda8aSnia * invoke the default entry, if there is one. 810bbfda8aSnia */ 820bbfda8aSnia if(action && ! strncmp(action, "WGOTO : ", 8)) { 830bbfda8aSnia GotoWorkSpaceByName(/* XXXXX */ Scr->currentvs, 840bbfda8aSnia ((char *)action) + 8); 850bbfda8aSnia } 860bbfda8aSnia else { 870bbfda8aSnia MenuItem *item; 880bbfda8aSnia 890bbfda8aSnia item = ActiveItem; 900bbfda8aSnia while(item && item->sub) { 910bbfda8aSnia if(!item->sub->defaultitem) { 920bbfda8aSnia break; 930bbfda8aSnia } 940bbfda8aSnia if(item->sub->defaultitem->func != F_MENU) { 950bbfda8aSnia break; 960bbfda8aSnia } 970bbfda8aSnia item = item->sub->defaultitem; 980bbfda8aSnia } 990bbfda8aSnia if(item && item->sub && item->sub->defaultitem) { 1000bbfda8aSnia ExecuteFunction(item->sub->defaultitem->func, 1010bbfda8aSnia item->sub->defaultitem->action, 1020bbfda8aSnia w, tmp_win, eventp, context, pulldown); 1030bbfda8aSnia } 1040bbfda8aSnia } 1050bbfda8aSnia} 1060bbfda8aSnia 1070bbfda8aSnia 1080bbfda8aSniaDFHANDLER(pin) 1090bbfda8aSnia{ 1100bbfda8aSnia if(! ActiveMenu) { 1110bbfda8aSnia return; 1120bbfda8aSnia } 1130bbfda8aSnia if(ActiveMenu->pinned) { 1140bbfda8aSnia XUnmapWindow(dpy, ActiveMenu->w); 1150bbfda8aSnia ActiveMenu->mapped = MRM_UNMAPPED; 1160bbfda8aSnia } 1170bbfda8aSnia else { 1180bbfda8aSnia XWindowAttributes attr; 1190bbfda8aSnia MenuRoot *menu; 1200bbfda8aSnia 1210bbfda8aSnia if(ActiveMenu->pmenu == NULL) { 1220bbfda8aSnia menu = malloc(sizeof(MenuRoot)); 1230bbfda8aSnia *menu = *ActiveMenu; 1240bbfda8aSnia menu->pinned = true; 1250bbfda8aSnia menu->mapped = MRM_NEVER; 1260bbfda8aSnia menu->width -= 10; 1270bbfda8aSnia if(menu->pull) { 1280bbfda8aSnia menu->width -= 16 + 10; 1290bbfda8aSnia } 1300bbfda8aSnia MakeMenu(menu); 1310bbfda8aSnia ActiveMenu->pmenu = menu; 1320bbfda8aSnia } 1330bbfda8aSnia else { 1340bbfda8aSnia menu = ActiveMenu->pmenu; 1350bbfda8aSnia } 1360bbfda8aSnia if(menu->mapped == MRM_MAPPED) { 1370bbfda8aSnia return; 1380bbfda8aSnia } 1390bbfda8aSnia XGetWindowAttributes(dpy, ActiveMenu->w, &attr); 1400bbfda8aSnia menu->x = attr.x; 1410bbfda8aSnia menu->y = attr.y; 1420bbfda8aSnia XMoveWindow(dpy, menu->w, menu->x, menu->y); 1430bbfda8aSnia XMapRaised(dpy, menu->w); 1440bbfda8aSnia menu->mapped = MRM_MAPPED; 1450bbfda8aSnia } 1460bbfda8aSnia PopDownMenu(); 1470bbfda8aSnia} 1480bbfda8aSnia 1490bbfda8aSnia 1500bbfda8aSnia 1510bbfda8aSnia/* 1520bbfda8aSnia * Alternate keymaps/contexts 1530bbfda8aSnia */ 1540bbfda8aSniaDFHANDLER(altkeymap) 1550bbfda8aSnia{ 1560bbfda8aSnia int alt, stat_; 1570bbfda8aSnia 1580bbfda8aSnia if(! action) { 1590bbfda8aSnia return; 1600bbfda8aSnia } 1610bbfda8aSnia stat_ = sscanf(action, "%d", &alt); 1620bbfda8aSnia if(stat_ != 1) { 1630bbfda8aSnia return; 1640bbfda8aSnia } 1650bbfda8aSnia if((alt < 1) || (alt > 5)) { 1660bbfda8aSnia return; 1670bbfda8aSnia } 1680bbfda8aSnia AlternateKeymap = Alt1Mask << (alt - 1); 1690bbfda8aSnia XGrabPointer(dpy, Scr->Root, True, ButtonPressMask | ButtonReleaseMask, 1700bbfda8aSnia GrabModeAsync, GrabModeAsync, 1710bbfda8aSnia Scr->Root, Scr->AlterCursor, CurrentTime); 1720bbfda8aSnia func_reset_cursor = false; // Leave special cursor alone 1730bbfda8aSnia XGrabKeyboard(dpy, Scr->Root, True, GrabModeAsync, GrabModeAsync, CurrentTime); 1740bbfda8aSnia return; 1750bbfda8aSnia} 1760bbfda8aSnia 1770bbfda8aSniaDFHANDLER(altcontext) 1780bbfda8aSnia{ 1790bbfda8aSnia AlternateContext = true; 1800bbfda8aSnia XGrabPointer(dpy, Scr->Root, False, ButtonPressMask | ButtonReleaseMask, 1810bbfda8aSnia GrabModeAsync, GrabModeAsync, 1820bbfda8aSnia Scr->Root, Scr->AlterCursor, CurrentTime); 1830bbfda8aSnia func_reset_cursor = false; // Leave special cursor alone 1840bbfda8aSnia XGrabKeyboard(dpy, Scr->Root, False, GrabModeAsync, GrabModeAsync, CurrentTime); 1850bbfda8aSnia return; 1860bbfda8aSnia} 1870bbfda8aSnia 1880bbfda8aSnia 1890bbfda8aSnia 1900bbfda8aSnia/* 1910bbfda8aSnia * A few trivial ctwm-control-ish meta-functions 1920bbfda8aSnia */ 1930bbfda8aSniaDFHANDLER(quit) 1940bbfda8aSnia{ 195b18c2d1eSnia DoShutdown(); 1960bbfda8aSnia} 1970bbfda8aSnia 1980bbfda8aSniaDFHANDLER(restart) 1990bbfda8aSnia{ 2000bbfda8aSnia DoRestart(eventp->xbutton.time); 2010bbfda8aSnia} 2020bbfda8aSnia 2030bbfda8aSniaDFHANDLER(beep) 2040bbfda8aSnia{ 2050bbfda8aSnia XBell(dpy, 0); 2060bbfda8aSnia} 2070bbfda8aSnia 2080bbfda8aSniaDFHANDLER(trace) 2090bbfda8aSnia{ 2100bbfda8aSnia DebugTrace(action); 2110bbfda8aSnia} 2120bbfda8aSnia 2130bbfda8aSnia 2140bbfda8aSnia 215b18c2d1eSnia#ifdef WINBOX 2160bbfda8aSnia/* 2170bbfda8aSnia * Special windowbox-related 2180bbfda8aSnia */ 2190bbfda8aSniaDFHANDLER(fittocontent) 2200bbfda8aSnia{ 2210bbfda8aSnia if(!tmp_win->iswinbox) { 2220bbfda8aSnia XBell(dpy, 0); 2230bbfda8aSnia return; 2240bbfda8aSnia } 2250bbfda8aSnia fittocontent(tmp_win); 2260bbfda8aSnia} 227b18c2d1eSnia#endif 2280bbfda8aSnia 2290bbfda8aSnia 2300bbfda8aSnia 2310bbfda8aSnia/* 2320bbfda8aSnia * A few things that are sorta windows/icons related, but don't really 2330bbfda8aSnia * fit with the window-targetted things in functions_win. 2340bbfda8aSnia */ 2350bbfda8aSniaDFHANDLER(showbackground) 2360bbfda8aSnia{ 2370bbfda8aSnia ShowBackground(Scr->currentvs, -1); 2380bbfda8aSnia} 2390bbfda8aSnia 2400bbfda8aSniaDFHANDLER(raiseicons) 2410bbfda8aSnia{ 2420bbfda8aSnia for(TwmWindow *t = Scr->FirstWindow; t != NULL; t = t->next) { 2430bbfda8aSnia if(t->icon && t->icon->w) { 2440bbfda8aSnia OtpRaise(t, IconWin); 2450bbfda8aSnia } 2460bbfda8aSnia } 2470bbfda8aSnia} 2480bbfda8aSnia 2490bbfda8aSniaDFHANDLER(rescuewindows) 2500bbfda8aSnia{ 2510bbfda8aSnia RescueWindows(); 2520bbfda8aSnia} 2530bbfda8aSnia 2540bbfda8aSnia 2550bbfda8aSnia 2560bbfda8aSnia/* 2570bbfda8aSnia * Despite the name, this is more like 'gotoworkspace' than the other 2580bbfda8aSnia * 'warpto*' funcs, as it's just about switching your view, not anything 2590bbfda8aSnia * going to a window. 2600bbfda8aSnia */ 2610bbfda8aSniastatic void 2620bbfda8aSniaWarpToScreen(int n, int inc) 2630bbfda8aSnia{ 2640bbfda8aSnia Window dumwin; 2650bbfda8aSnia int x, y, dumint; 2660bbfda8aSnia unsigned int dummask; 2670bbfda8aSnia ScreenInfo *newscr = NULL; 2680bbfda8aSnia 2690bbfda8aSnia while(!newscr) { 2700bbfda8aSnia /* wrap around */ 2710bbfda8aSnia if(n < 0) { 2720bbfda8aSnia n = NumScreens - 1; 2730bbfda8aSnia } 2740bbfda8aSnia else if(n >= NumScreens) { 2750bbfda8aSnia n = 0; 2760bbfda8aSnia } 2770bbfda8aSnia 2780bbfda8aSnia newscr = ScreenList[n]; 2790bbfda8aSnia if(!newscr) { /* make sure screen is managed */ 2800bbfda8aSnia if(inc) { /* walk around the list */ 2810bbfda8aSnia n += inc; 2820bbfda8aSnia continue; 2830bbfda8aSnia } 2840bbfda8aSnia fprintf(stderr, "%s: unable to warp to unmanaged screen %d\n", 2850bbfda8aSnia ProgramName, n); 2860bbfda8aSnia XBell(dpy, 0); 2870bbfda8aSnia return; 2880bbfda8aSnia } 2890bbfda8aSnia } 2900bbfda8aSnia 2910bbfda8aSnia if(Scr->screen == n) { 2920bbfda8aSnia return; /* already on that screen */ 2930bbfda8aSnia } 2940bbfda8aSnia 2950bbfda8aSnia PreviousScreen = Scr->screen; 2960bbfda8aSnia XQueryPointer(dpy, Scr->Root, &dumwin, &dumwin, &x, &y, 2970bbfda8aSnia &dumint, &dumint, &dummask); 2980bbfda8aSnia 2990bbfda8aSnia XWarpPointer(dpy, None, newscr->Root, 0, 0, 0, 0, x, y); 3000bbfda8aSnia Scr = newscr; 3010bbfda8aSnia return; 3020bbfda8aSnia} 3030bbfda8aSnia 3040bbfda8aSniaDFHANDLER(warptoscreen) 3050bbfda8aSnia{ 3060bbfda8aSnia if(strcmp(action, WARPSCREEN_NEXT) == 0) { 3070bbfda8aSnia WarpToScreen(Scr->screen + 1, 1); 3080bbfda8aSnia } 3090bbfda8aSnia else if(strcmp(action, WARPSCREEN_PREV) == 0) { 3100bbfda8aSnia WarpToScreen(Scr->screen - 1, -1); 3110bbfda8aSnia } 3120bbfda8aSnia else if(strcmp(action, WARPSCREEN_BACK) == 0) { 3130bbfda8aSnia WarpToScreen(PreviousScreen, 0); 3140bbfda8aSnia } 3150bbfda8aSnia else { 3160bbfda8aSnia WarpToScreen(atoi(action), 0); 3170bbfda8aSnia } 3180bbfda8aSnia} 3190bbfda8aSnia 3200bbfda8aSnia 3210bbfda8aSnia 3220bbfda8aSnia/* 3230bbfda8aSnia * Sound-related 3240bbfda8aSnia */ 3250bbfda8aSnia#ifdef SOUNDS 3260bbfda8aSniaDFHANDLER(togglesound) 3270bbfda8aSnia{ 3280bbfda8aSnia toggle_sound(); 3290bbfda8aSnia} 3300bbfda8aSnia 3310bbfda8aSniaDFHANDLER(rereadsounds) 3320bbfda8aSnia{ 3330bbfda8aSnia reread_sounds(); 3340bbfda8aSnia} 3350bbfda8aSnia#endif 3360bbfda8aSnia 3370bbfda8aSnia 3380bbfda8aSnia 3390bbfda8aSnia/* 3400bbfda8aSnia * And executing an external program 3410bbfda8aSnia */ 3420bbfda8aSniastatic void Execute(const char *_s); 3430bbfda8aSnia 3440bbfda8aSniaDFHANDLER(exec) 3450bbfda8aSnia{ 3460bbfda8aSnia PopDownMenu(); 3470bbfda8aSnia if(!Scr->NoGrabServer) { 3480bbfda8aSnia XUngrabServer(dpy); 3490bbfda8aSnia XSync(dpy, 0); 3500bbfda8aSnia } 3510bbfda8aSnia XUngrabPointer(dpy, CurrentTime); 3520bbfda8aSnia XSync(dpy, 0); 3530bbfda8aSnia Execute(action); 3540bbfda8aSnia} 3550bbfda8aSnia 3560bbfda8aSnia 3570bbfda8aSniastatic void 3580bbfda8aSniaExecute(const char *_s) 3590bbfda8aSnia{ 3600bbfda8aSnia char *s; 3610bbfda8aSnia char *_ds; 3620bbfda8aSnia char *orig_display; 3630bbfda8aSnia int restorevar = 0; 3640bbfda8aSnia char *subs; 3650bbfda8aSnia 3660bbfda8aSnia /* Seatbelt */ 3670bbfda8aSnia if(!_s) { 3680bbfda8aSnia return; 3690bbfda8aSnia } 3700bbfda8aSnia 3710bbfda8aSnia /* Work on a local copy since we're mutating it */ 3720bbfda8aSnia s = strdup(_s); 3730bbfda8aSnia if(!s) { 3740bbfda8aSnia return; 3750bbfda8aSnia } 3760bbfda8aSnia 3770bbfda8aSnia /* Stash up current $DISPLAY value for resetting */ 3780bbfda8aSnia orig_display = getenv("DISPLAY"); 3790bbfda8aSnia 3800bbfda8aSnia 3810bbfda8aSnia /* 3820bbfda8aSnia * Build a display string using the current screen number, so that 3830bbfda8aSnia * X programs which get fired up from a menu come up on the screen 3840bbfda8aSnia * that they were invoked from, unless specifically overridden on 3850bbfda8aSnia * their command line. 3860bbfda8aSnia * 3870bbfda8aSnia * Which is to say, given that we're on display "foo.bar:1.2", we 3880bbfda8aSnia * want to translate that into "foo.bar:1.{Scr->screen}". 3890bbfda8aSnia * 3900bbfda8aSnia * We strdup() because DisplayString() is a macro returning into the 3910bbfda8aSnia * dpy structure, and we're going to mutate the value we get from it. 3920bbfda8aSnia */ 3930bbfda8aSnia _ds = DisplayString(dpy); 3940bbfda8aSnia if(_ds) { 3950bbfda8aSnia char *ds; 3960bbfda8aSnia char *colon; 3970bbfda8aSnia 3980bbfda8aSnia ds = strdup(_ds); 3990bbfda8aSnia if(!ds) { 4000bbfda8aSnia goto end_execute; 4010bbfda8aSnia } 4020bbfda8aSnia 4030bbfda8aSnia /* If it's not host:dpy, we don't have anything to do here */ 4040bbfda8aSnia colon = strrchr(ds, ':'); 4050bbfda8aSnia if(colon) { 4060bbfda8aSnia char *dot, *new_display; 4070bbfda8aSnia 4080bbfda8aSnia /* Find the . in display.screen and chop it off */ 4090bbfda8aSnia dot = strchr(colon, '.'); 4100bbfda8aSnia if(dot) { 4110bbfda8aSnia *dot = '\0'; 4120bbfda8aSnia } 4130bbfda8aSnia 4140bbfda8aSnia /* Build a new string with our correct screen info */ 4150bbfda8aSnia asprintf(&new_display, "%s.%d", ds, Scr->screen); 4160bbfda8aSnia if(!new_display) { 4170bbfda8aSnia free(ds); 4180bbfda8aSnia goto end_execute; 4190bbfda8aSnia } 4200bbfda8aSnia 4210bbfda8aSnia /* And set */ 4220bbfda8aSnia setenv("DISPLAY", new_display, 1); 4230bbfda8aSnia free(new_display); 4240bbfda8aSnia restorevar = 1; 4250bbfda8aSnia } 4260bbfda8aSnia free(ds); 4270bbfda8aSnia } 4280bbfda8aSnia 4290bbfda8aSnia 4300bbfda8aSnia /* 4310bbfda8aSnia * We replace a couple placeholders in the string. $currentworkspace 4320bbfda8aSnia * is documented in the manual; $redirect is not. 4330bbfda8aSnia */ 4340bbfda8aSnia subs = strstr(s, "$currentworkspace"); 4350bbfda8aSnia if(subs) { 4360bbfda8aSnia char *tmp; 4370bbfda8aSnia char *wsname; 4380bbfda8aSnia 4390bbfda8aSnia wsname = GetCurrentWorkSpaceName(Scr->currentvs); 4400bbfda8aSnia if(!wsname) { 4410bbfda8aSnia wsname = ""; 4420bbfda8aSnia } 4430bbfda8aSnia 4440bbfda8aSnia tmp = replace_substr(s, "$currentworkspace", wsname); 4450bbfda8aSnia if(!tmp) { 4460bbfda8aSnia goto end_execute; 4470bbfda8aSnia } 4480bbfda8aSnia free(s); 4490bbfda8aSnia s = tmp; 4500bbfda8aSnia } 4510bbfda8aSnia 452b18c2d1eSnia#ifdef CAPTIVE 4530bbfda8aSnia subs = strstr(s, "$redirect"); 4540bbfda8aSnia if(subs) { 4550bbfda8aSnia char *tmp; 4560bbfda8aSnia char *redir; 4570bbfda8aSnia 4580bbfda8aSnia if(CLarg.is_captive) { 4590bbfda8aSnia asprintf(&redir, "-xrm 'ctwm.redirect:%s'", Scr->captivename); 4600bbfda8aSnia if(!redir) { 4610bbfda8aSnia goto end_execute; 4620bbfda8aSnia } 4630bbfda8aSnia } 4640bbfda8aSnia else { 4650bbfda8aSnia redir = malloc(1); 4660bbfda8aSnia *redir = '\0'; 4670bbfda8aSnia } 4680bbfda8aSnia 4690bbfda8aSnia tmp = replace_substr(s, "$redirect", redir); 4700bbfda8aSnia free(s); 4710bbfda8aSnia s = tmp; 4720bbfda8aSnia 4730bbfda8aSnia free(redir); 4740bbfda8aSnia } 475b18c2d1eSnia#endif 4760bbfda8aSnia 4770bbfda8aSnia 4780bbfda8aSnia /* 4790bbfda8aSnia * Call it. Return value doesn't really matter, since whatever 4800bbfda8aSnia * happened we're done. Maybe someday if we develop a "show user 4810bbfda8aSnia * message" generalized func, we can tell the user if executing 4820bbfda8aSnia * failed somehow. 4830bbfda8aSnia */ 4840bbfda8aSnia system(s); 4850bbfda8aSnia 4860bbfda8aSnia 4870bbfda8aSnia /* 4880bbfda8aSnia * Restore $DISPLAY if we changed it. It's probably only necessary 4890bbfda8aSnia * in edge cases (it might be used by ctwm restarting itself, for 4900bbfda8aSnia * instance) and it's not quite clear whether the DisplayString() 4910bbfda8aSnia * result would even be wrong for that, but what the heck, setenv() 4920bbfda8aSnia * is cheap. 4930bbfda8aSnia */ 4940bbfda8aSnia if(restorevar) { 4950bbfda8aSnia if(orig_display) { 4960bbfda8aSnia setenv("DISPLAY", orig_display, 1); 4970bbfda8aSnia } 4980bbfda8aSnia else { 4990bbfda8aSnia unsetenv("DISPLAY"); 5000bbfda8aSnia } 5010bbfda8aSnia } 5020bbfda8aSnia 5030bbfda8aSnia 5040bbfda8aSnia /* Clean up */ 5050bbfda8aSniaend_execute: 5060bbfda8aSnia free(s); 5070bbfda8aSnia return; 5080bbfda8aSnia} 509