1/* 2 * Misc function implementation 3 * 4 * These are things that either don't fit neatly into another category, 5 * or fit into a category too small to be worth making individual files 6 * for. 7 */ 8 9#include "ctwm.h" 10 11#include <stdlib.h> 12 13#include "animate.h" 14#include "ctwm_shutdown.h" 15#include "functions.h" 16#include "functions_defs.h" 17#include "functions_internal.h" 18#include "icons.h" 19#include "otp.h" 20#include "screen.h" 21#ifdef SOUNDS 22#include "sound.h" 23#endif 24#include "util.h" 25#include "win_iconify.h" 26#ifdef WINBOX 27#include "windowbox.h" 28#endif 29#include "workspace_utils.h" 30 31#include "ext/repl_str.h" 32 33 34 35/* 36 * Animation-related 37 */ 38DFHANDLER(startanimation) 39{ 40 StartAnimation(); 41} 42 43DFHANDLER(stopanimation) 44{ 45 StopAnimation(); 46} 47 48DFHANDLER(speedupanimation) 49{ 50 ModifyAnimationSpeed(1); 51} 52 53DFHANDLER(slowdownanimation) 54{ 55 ModifyAnimationSpeed(-1); 56} 57 58 59 60/* 61 * Menu-related 62 */ 63DFHANDLER(menu) 64{ 65 /* 66 * n.b.: The f.menu handler is all kinds of magic; it's actually 67 * completely unrelated to pulling up the menu. 68 * 69 * When a button/key binding invokes f.menu to open up a menu, that's 70 * actually handled in the KeyPress or ButtonPress handlers by 71 * calling do{_key,}_menu(). When we descend into a submenu, that's 72 * handled in KeyPress handler for keyboard navigation when we hit 73 * the Right arrow, or inside the 74 * event loop recapture in UpdateMenu() for mouse navigation when we 75 * move it to the right side of the menu entry. 76 * 77 * This handler is only used by "invoking" a menu item; releasing the 78 * mouse button on the left side without moving right to open out the 79 * submenu, or hitting the Enter key. All it does is immediately 80 * invoke the default entry, if there is one. 81 */ 82 if(action && ! strncmp(action, "WGOTO : ", 8)) { 83 GotoWorkSpaceByName(/* XXXXX */ Scr->currentvs, 84 ((char *)action) + 8); 85 } 86 else { 87 MenuItem *item; 88 89 item = ActiveItem; 90 while(item && item->sub) { 91 if(!item->sub->defaultitem) { 92 break; 93 } 94 if(item->sub->defaultitem->func != F_MENU) { 95 break; 96 } 97 item = item->sub->defaultitem; 98 } 99 if(item && item->sub && item->sub->defaultitem) { 100 ExecuteFunction(item->sub->defaultitem->func, 101 item->sub->defaultitem->action, 102 w, tmp_win, eventp, context, pulldown); 103 } 104 } 105} 106 107 108DFHANDLER(pin) 109{ 110 if(! ActiveMenu) { 111 return; 112 } 113 if(ActiveMenu->pinned) { 114 XUnmapWindow(dpy, ActiveMenu->w); 115 ActiveMenu->mapped = MRM_UNMAPPED; 116 } 117 else { 118 XWindowAttributes attr; 119 MenuRoot *menu; 120 121 if(ActiveMenu->pmenu == NULL) { 122 menu = malloc(sizeof(MenuRoot)); 123 *menu = *ActiveMenu; 124 menu->pinned = true; 125 menu->mapped = MRM_NEVER; 126 menu->width -= 10; 127 if(menu->pull) { 128 menu->width -= 16 + 10; 129 } 130 MakeMenu(menu); 131 ActiveMenu->pmenu = menu; 132 } 133 else { 134 menu = ActiveMenu->pmenu; 135 } 136 if(menu->mapped == MRM_MAPPED) { 137 return; 138 } 139 XGetWindowAttributes(dpy, ActiveMenu->w, &attr); 140 menu->x = attr.x; 141 menu->y = attr.y; 142 XMoveWindow(dpy, menu->w, menu->x, menu->y); 143 XMapRaised(dpy, menu->w); 144 menu->mapped = MRM_MAPPED; 145 } 146 PopDownMenu(); 147} 148 149 150 151/* 152 * Alternate keymaps/contexts 153 */ 154DFHANDLER(altkeymap) 155{ 156 int alt, stat_; 157 158 if(! action) { 159 return; 160 } 161 stat_ = sscanf(action, "%d", &alt); 162 if(stat_ != 1) { 163 return; 164 } 165 if((alt < 1) || (alt > 5)) { 166 return; 167 } 168 AlternateKeymap = Alt1Mask << (alt - 1); 169 XGrabPointer(dpy, Scr->Root, True, ButtonPressMask | ButtonReleaseMask, 170 GrabModeAsync, GrabModeAsync, 171 Scr->Root, Scr->AlterCursor, CurrentTime); 172 func_reset_cursor = false; // Leave special cursor alone 173 XGrabKeyboard(dpy, Scr->Root, True, GrabModeAsync, GrabModeAsync, CurrentTime); 174 return; 175} 176 177DFHANDLER(altcontext) 178{ 179 AlternateContext = true; 180 XGrabPointer(dpy, Scr->Root, False, ButtonPressMask | ButtonReleaseMask, 181 GrabModeAsync, GrabModeAsync, 182 Scr->Root, Scr->AlterCursor, CurrentTime); 183 func_reset_cursor = false; // Leave special cursor alone 184 XGrabKeyboard(dpy, Scr->Root, False, GrabModeAsync, GrabModeAsync, CurrentTime); 185 return; 186} 187 188 189 190/* 191 * A few trivial ctwm-control-ish meta-functions 192 */ 193DFHANDLER(quit) 194{ 195 DoShutdown(); 196} 197 198DFHANDLER(restart) 199{ 200 DoRestart(eventp->xbutton.time); 201} 202 203DFHANDLER(beep) 204{ 205 XBell(dpy, 0); 206} 207 208DFHANDLER(trace) 209{ 210 DebugTrace(action); 211} 212 213 214 215#ifdef WINBOX 216/* 217 * Special windowbox-related 218 */ 219DFHANDLER(fittocontent) 220{ 221 if(!tmp_win->iswinbox) { 222 XBell(dpy, 0); 223 return; 224 } 225 fittocontent(tmp_win); 226} 227#endif 228 229 230 231/* 232 * A few things that are sorta windows/icons related, but don't really 233 * fit with the window-targetted things in functions_win. 234 */ 235DFHANDLER(showbackground) 236{ 237 ShowBackground(Scr->currentvs, -1); 238} 239 240DFHANDLER(raiseicons) 241{ 242 for(TwmWindow *t = Scr->FirstWindow; t != NULL; t = t->next) { 243 if(t->icon && t->icon->w) { 244 OtpRaise(t, IconWin); 245 } 246 } 247} 248 249DFHANDLER(rescuewindows) 250{ 251 RescueWindows(); 252} 253 254 255 256/* 257 * Despite the name, this is more like 'gotoworkspace' than the other 258 * 'warpto*' funcs, as it's just about switching your view, not anything 259 * going to a window. 260 */ 261static void 262WarpToScreen(int n, int inc) 263{ 264 Window dumwin; 265 int x, y, dumint; 266 unsigned int dummask; 267 ScreenInfo *newscr = NULL; 268 269 while(!newscr) { 270 /* wrap around */ 271 if(n < 0) { 272 n = NumScreens - 1; 273 } 274 else if(n >= NumScreens) { 275 n = 0; 276 } 277 278 newscr = ScreenList[n]; 279 if(!newscr) { /* make sure screen is managed */ 280 if(inc) { /* walk around the list */ 281 n += inc; 282 continue; 283 } 284 fprintf(stderr, "%s: unable to warp to unmanaged screen %d\n", 285 ProgramName, n); 286 XBell(dpy, 0); 287 return; 288 } 289 } 290 291 if(Scr->screen == n) { 292 return; /* already on that screen */ 293 } 294 295 PreviousScreen = Scr->screen; 296 XQueryPointer(dpy, Scr->Root, &dumwin, &dumwin, &x, &y, 297 &dumint, &dumint, &dummask); 298 299 XWarpPointer(dpy, None, newscr->Root, 0, 0, 0, 0, x, y); 300 Scr = newscr; 301 return; 302} 303 304DFHANDLER(warptoscreen) 305{ 306 if(strcmp(action, WARPSCREEN_NEXT) == 0) { 307 WarpToScreen(Scr->screen + 1, 1); 308 } 309 else if(strcmp(action, WARPSCREEN_PREV) == 0) { 310 WarpToScreen(Scr->screen - 1, -1); 311 } 312 else if(strcmp(action, WARPSCREEN_BACK) == 0) { 313 WarpToScreen(PreviousScreen, 0); 314 } 315 else { 316 WarpToScreen(atoi(action), 0); 317 } 318} 319 320 321 322/* 323 * Sound-related 324 */ 325#ifdef SOUNDS 326DFHANDLER(togglesound) 327{ 328 toggle_sound(); 329} 330 331DFHANDLER(rereadsounds) 332{ 333 reread_sounds(); 334} 335#endif 336 337 338 339/* 340 * And executing an external program 341 */ 342static void Execute(const char *_s); 343 344DFHANDLER(exec) 345{ 346 PopDownMenu(); 347 if(!Scr->NoGrabServer) { 348 XUngrabServer(dpy); 349 XSync(dpy, 0); 350 } 351 XUngrabPointer(dpy, CurrentTime); 352 XSync(dpy, 0); 353 Execute(action); 354} 355 356 357static void 358Execute(const char *_s) 359{ 360 char *s; 361 char *_ds; 362 char *orig_display; 363 int restorevar = 0; 364 char *subs; 365 366 /* Seatbelt */ 367 if(!_s) { 368 return; 369 } 370 371 /* Work on a local copy since we're mutating it */ 372 s = strdup(_s); 373 if(!s) { 374 return; 375 } 376 377 /* Stash up current $DISPLAY value for resetting */ 378 orig_display = getenv("DISPLAY"); 379 380 381 /* 382 * Build a display string using the current screen number, so that 383 * X programs which get fired up from a menu come up on the screen 384 * that they were invoked from, unless specifically overridden on 385 * their command line. 386 * 387 * Which is to say, given that we're on display "foo.bar:1.2", we 388 * want to translate that into "foo.bar:1.{Scr->screen}". 389 * 390 * We strdup() because DisplayString() is a macro returning into the 391 * dpy structure, and we're going to mutate the value we get from it. 392 */ 393 _ds = DisplayString(dpy); 394 if(_ds) { 395 char *ds; 396 char *colon; 397 398 ds = strdup(_ds); 399 if(!ds) { 400 goto end_execute; 401 } 402 403 /* If it's not host:dpy, we don't have anything to do here */ 404 colon = strrchr(ds, ':'); 405 if(colon) { 406 char *dot, *new_display; 407 408 /* Find the . in display.screen and chop it off */ 409 dot = strchr(colon, '.'); 410 if(dot) { 411 *dot = '\0'; 412 } 413 414 /* Build a new string with our correct screen info */ 415 asprintf(&new_display, "%s.%d", ds, Scr->screen); 416 if(!new_display) { 417 free(ds); 418 goto end_execute; 419 } 420 421 /* And set */ 422 setenv("DISPLAY", new_display, 1); 423 free(new_display); 424 restorevar = 1; 425 } 426 free(ds); 427 } 428 429 430 /* 431 * We replace a couple placeholders in the string. $currentworkspace 432 * is documented in the manual; $redirect is not. 433 */ 434 subs = strstr(s, "$currentworkspace"); 435 if(subs) { 436 char *tmp; 437 char *wsname; 438 439 wsname = GetCurrentWorkSpaceName(Scr->currentvs); 440 if(!wsname) { 441 wsname = ""; 442 } 443 444 tmp = replace_substr(s, "$currentworkspace", wsname); 445 if(!tmp) { 446 goto end_execute; 447 } 448 free(s); 449 s = tmp; 450 } 451 452#ifdef CAPTIVE 453 subs = strstr(s, "$redirect"); 454 if(subs) { 455 char *tmp; 456 char *redir; 457 458 if(CLarg.is_captive) { 459 asprintf(&redir, "-xrm 'ctwm.redirect:%s'", Scr->captivename); 460 if(!redir) { 461 goto end_execute; 462 } 463 } 464 else { 465 redir = malloc(1); 466 *redir = '\0'; 467 } 468 469 tmp = replace_substr(s, "$redirect", redir); 470 free(s); 471 s = tmp; 472 473 free(redir); 474 } 475#endif 476 477 478 /* 479 * Call it. Return value doesn't really matter, since whatever 480 * happened we're done. Maybe someday if we develop a "show user 481 * message" generalized func, we can tell the user if executing 482 * failed somehow. 483 */ 484 system(s); 485 486 487 /* 488 * Restore $DISPLAY if we changed it. It's probably only necessary 489 * in edge cases (it might be used by ctwm restarting itself, for 490 * instance) and it's not quite clear whether the DisplayString() 491 * result would even be wrong for that, but what the heck, setenv() 492 * is cheap. 493 */ 494 if(restorevar) { 495 if(orig_display) { 496 setenv("DISPLAY", orig_display, 1); 497 } 498 else { 499 unsetenv("DISPLAY"); 500 } 501 } 502 503 504 /* Clean up */ 505end_execute: 506 free(s); 507 return; 508} 509