1/* 2 * Dispatcher for our f.whatever functions 3 */ 4 5 6#include "ctwm.h" 7 8#include <stdio.h> 9 10#include "events.h" 11#include "functions.h" 12#include "functions_defs.h" 13#include "functions_deferral.h" // Generated deferral table 14#include "functions_internal.h" 15#include "screen.h" 16 17 18static DFHANDLER(nop); 19static DFHANDLER(separator); 20static DFHANDLER(title); 21static DFHANDLER(deltastop); 22static DFHANDLER(function); 23 24/* 25 * The generated dispatch table. Have to do this after the preceeding 26 * prototypes for the handers in this file, since those funcs are ref'd 27 * in this table. 28 */ 29#include "functions_dispatch_execution.h" 30 31 32/* 33 * Various functions can be executed "from the root" (which generally 34 * means "from a menu"), but apply to a specific window (e.g., f.move, 35 * f.identify, etc). You obviously can't be selecting it from a menu and 36 * pointing at the window to target at the same time, so we have to 37 * 2-step those calls. This happens via the DeferExecution() call in the 38 * implementations of those functions, which stashes the "in progress" 39 * function in RootFunction. The HandleButtonPress() event handler will 40 * later notice that and loop events back around into ExecuteFunction() 41 * again to pick up where it left off. 42 * 43 * (a more descriptive name might be in order) 44 */ 45int RootFunction = 0; 46 47 48/* 49 * Track whether a window gets moved by move operations: used for 50 * f.deltastop handling. 51 */ 52bool WindowMoved = false; 53 54/* 55 * Whether the cursor needs to be reset from a way we've altered it in 56 * the process of running functions. This is used to determine whether 57 * we're ungrabbing the pointer to reset back from setting the WaitCursor 58 * early on in the execution process. X-ref the XXX comment on that; 59 * it's unclear as to whether we should even be doing this anymore, but 60 * since we are, we use a global to ease tracking whether we need to 61 * unset it. There are cases deeper down in function handling that may 62 * do their own fudgery and want the pointer left alone after they 63 * return. 64 */ 65bool func_reset_cursor; 66 67/* 68 * Time of various actions: used in ConstrainedMoveTime related bits in 69 * some window moving/resizing. 70 */ 71Time last_time = 0; 72 73 74static bool EF_main(EF_FULLPROTO); 75 76static bool DeferExecution(int context, int func, Cursor cursor); 77static bool should_defer(int func); 78static Cursor defer_cursor(int func); 79static Cursor NeedToDefer(MenuRoot *root); 80 81 82/*********************************************************************** 83 * 84 * Procedure: 85 * ExecuteFunction - execute a twm root function 86 * 87 * Inputs: 88 * func - the function to execute 89 * action - the menu action to execute 90 * w - the window to execute this function on 91 * tmp_win - the twm window structure 92 * event - the event that caused the function 93 * context - the context in which the button was pressed 94 * pulldown- flag indicating execution from pull down menu 95 * 96 *********************************************************************** 97 */ 98void 99ExecuteFunction(EF_FULLPROTO) 100{ 101 EF_main(EF_ARGS); 102} 103 104/* 105 * Main ExecuteFunction body; returns true if we should continue a 106 * f.function's progress, false if we should stop. 107 * 108 * This is separate because only the recursive calls in f.function 109 * handling care about that return. The only possible way to get to a 110 * false return is via f.deltastop triggering. We can't do it just with 111 * a global, since f.function can at least in theory happen recursively; 112 * I don't know how well it would actually work, but it has a chance. 113 */ 114static bool 115EF_main(EF_FULLPROTO) 116{ 117 /* This should always start out clear when we come in here */ 118 RootFunction = 0; 119 120 /* Early escape for cutting out of things */ 121 if(Cancel) { 122 /* 123 * Strictly, this could probably be false, since if it's set it 124 * would mean it'll just happen again when we iterate back 125 * through for the next action. Once set, it only gets unset in 126 * the ButtonRelease handler, which I don't think would ever get 127 * called in between pieces of a f.function call. But I can't be 128 * sure, so just go ahead and return true, and we'll eat a few 129 * extra loops of function calls and insta-returns if it happens. 130 */ 131 return true; 132 } 133 134 135 /* 136 * More early escapes; some "functions" don't actually do anything 137 * when executed, and exist for magical purposes elsewhere. So just 138 * skip out early if we try running them. 139 */ 140 switch(func) { 141 case F_NOP: 142 case F_SEPARATOR: 143 case F_TITLE: 144 return true; 145 146 default: 147 ; /* FALLTHRU */ 148 } 149 150 151 /* 152 * Is this a function that needs some deferring? If so, go ahead and 153 * do that. Note that this specifically doesn't handle the special 154 * case of f.function; it has to do its own checking for whether 155 * there's something to defer. 156 */ 157 if(should_defer(func)) { 158 /* Figure the cursor */ 159 Cursor dc = defer_cursor(func); 160 if(dc == None) { 161 dc = Scr->SelectCursor; 162 } 163 164 /* And defer (if we're in a context that needs to) */ 165 if(DeferExecution(context, func, dc)) { 166 return true; 167 } 168 } 169 170 171 /* 172 * For most functions with a few exceptions, grab the pointer. 173 * 174 * This is actually not a grab so much to take control of the 175 * pointer, as to set the cursor. Apparently, xlib doesn't 176 * distinguish the two. The functions that need it in a "take 177 * control" sense (like the move and resize bits) should all be doing 178 * their own explicit grabs to handle that. 179 * 180 * XXX I have no idea why there's the exclusion list. Apart from 181 * adding 1 or 2 functions, this code comes verbatim from twm, which 182 * has no history or documentation as to why it's happening. 183 * 184 * XXX I'm not sure this is even worth doing anymore. The point of 185 * the WaitCursor is to let the user know "yeah, I'm working on it", 186 * during operations that may take a while. On 1985 hardware, that 187 * would be "almost anything you do". But in the 21st century, what 188 * functions could fall into that category, and need to give some 189 * user feedback before either finishing or doing something that 190 * gives other user feedback anyway? 191 */ 192 func_reset_cursor = false; 193 switch(func) { 194 case F_UPICONMGR: 195 case F_LEFTICONMGR: 196 case F_RIGHTICONMGR: 197 case F_DOWNICONMGR: 198 case F_FORWICONMGR: 199 case F_BACKICONMGR: 200 case F_NEXTICONMGR: 201 case F_PREVICONMGR: 202 case F_NOP: 203 case F_TITLE: 204 case F_DELTASTOP: 205 case F_RAISELOWER: 206 case F_WARPTOSCREEN: 207 case F_WARPTO: 208 case F_WARPRING: 209 case F_WARPTOICONMGR: 210 case F_COLORMAP: 211 case F_ALTKEYMAP: 212 case F_ALTCONTEXT: 213 break; 214 215 default: { 216 XGrabPointer(dpy, Scr->Root, True, 217 ButtonPressMask | ButtonReleaseMask, 218 GrabModeAsync, GrabModeAsync, 219 Scr->Root, Scr->WaitCursor, CurrentTime); 220 func_reset_cursor = true; 221 break; 222 } 223 } 224 225 226 /* 227 * Main dispatching/executing. 228 * 229 * _Most_ f.things are dispatched to individual handler functions, 230 * but we keep the magic related to f.function/f.deltastop out here 231 * to free the inner bits from having to care about the magic 232 * returns. 233 */ 234 switch(func) { 235 case F_DELTASTOP: 236 if(WindowMoved) { 237 /* 238 * If we're returning false here, it's because we were in 239 * the midst of a f.function, and we should stop. That 240 * means when we return from here it'll be into the false 241 * case in the F_FUNCTION handler below, which will break 242 * right out and fall through to the end of this 243 * function, which will do the post-function cleanup 244 * bits. That means we don't need to try and break out 245 * to them here, we can just return straight off. 246 */ 247 return false; 248 } 249 break; 250 251 case F_FUNCTION: { 252 MenuRoot *mroot; 253 MenuItem *mitem; 254 Cursor curs; 255 256 if((mroot = FindMenuRoot(action)) == NULL) { 257 if(!action) { 258 action = "undef"; 259 } 260 fprintf(stderr, "%s: couldn't find function \"%s\"\n", 261 ProgramName, (char *)action); 262 return true; 263 } 264 265 if((curs = NeedToDefer(mroot)) != None 266 && DeferExecution(context, func, curs)) { 267 return true; 268 } 269 else { 270 for(mitem = mroot->first; mitem != NULL; mitem = mitem->next) { 271 bool r = EF_main(mitem->func, mitem->action, w, 272 tmp_win, eventp, context, pulldown); 273 if(r == false) { 274 /* pebl FIXME: the focus should be updated here, 275 or the function would operate on the same window */ 276 break; 277 } 278 } 279 } 280 281 break; 282 } 283 284 285 /* 286 * Everything else is programmatically dispatched. 287 */ 288 default: { 289 if(func >= 0 && func < num_f_dis && func_dispatch[func] != NULL) { 290 (*func_dispatch[func])(EF_ARGS); 291 break; 292 } 293 294 /* 295 * Getting here means somehow it wasn't in the dispatch 296 * table, which shouldn't be possible without a big bug 297 * somewhere... 298 */ 299 fprintf(stderr, "Internal error: no handler for function %d\n", 300 func); 301 break; 302 } 303 } 304 305 306 307 /* 308 * Release the pointer. This should mostly mean actually "reset 309 * cursor", and be the complementary op to setting the cursor earlier 310 * up top. 311 * 312 * ButtonPressed == -1 means that we didn't get here via some sort of 313 * mouse clickery. If we did, then we presume that has some 314 * ownership of the pointer we don't want to relinquish yet. And we 315 * don't have to, as the ButtonRelease handler will take care of 316 * things when it fires anyway. 317 * 318 * This has a similar XXX to the cursor setting earlier, as to 319 * whether it ought to exist. 320 */ 321 if(func_reset_cursor && ButtonPressed == -1) { 322 XUngrabPointer(dpy, CurrentTime); 323 func_reset_cursor = false; 324 } 325 326 return true; 327} 328 329 330 331/* 332 * Implementation of function deferral 333 */ 334 335/* 336 * Setting a last cursor and re-grabbing to it. This is used in the 337 * AddWindow() process. It might grab the mouse and re-set the 338 * cursor away from us, and so it needs a way to set it back. 339 * 340 * XXX This begs for renaming... 341 */ 342static Cursor LastCursor; 343 344void 345ReGrab(void) 346{ 347 XGrabPointer(dpy, Scr->Root, True, 348 ButtonPressMask | ButtonReleaseMask, 349 GrabModeAsync, GrabModeAsync, 350 Scr->Root, LastCursor, CurrentTime); 351} 352 353 354/* 355 * Check to see if a function (implicitly, a window-targetting function) 356 * is happening in a context away from an actual window, and if so stash 357 * up info about what's in progress and return true to tell the caller to 358 * end processing the function (for now). X-ref comment on RootFunction 359 * variable definition for details. 360 * 361 * Inputs: 362 * context - the context in which the mouse button was pressed 363 * func - the function to defer 364 * cursor - the cursor to display while waiting 365 */ 366static bool 367DeferExecution(int context, int func, Cursor cursor) 368{ 369 Window confine_to = Scr->Root; 370#ifdef CAPTIVE 371 if(func == F_ADOPTWINDOW) { 372 confine_to = None; 373 } 374#endif 375 376 if((context == C_ROOT) || (context == C_ALTERNATE)) { 377 LastCursor = cursor; 378 XGrabPointer(dpy, 379 Scr->Root, 380 True, 381 ButtonPressMask | ButtonReleaseMask, 382 GrabModeAsync, 383 GrabModeAsync, 384 confine_to, 385 cursor, 386 CurrentTime); 387 RootFunction = func; 388 389 return true; 390 } 391 392 return false; 393} 394 395 396/* 397 * Various determinates of whether a function should be deferred if its 398 * called in a general (rather than win-specific) context, and what 399 * cursor should be used in the meantime. 400 * 401 * We define a big lookup array to do it. We have to indirect through an 402 * intermediate enum value instead of just the cursor since it isn't 403 * available at compile time, and we can't just make it a pointer into 404 * Scr since there are [potentially] multiple Scr's anyway. And we need 405 * an explicit unused DC_NONE value so our real values are all non-zero; 406 * the ones we don't explicitly set get initialized to 0, which we can 407 * then take as a flag saying "we don't defer this func". 408 * 409 * fdef_table in functions_deferral.h generated from functions_defs.list. 410 */ 411 412static bool 413should_defer(int func) 414{ 415 /* Outside the table -> "No" */ 416 if(func < 0 || func >= fdef_table_max) { 417 return false; 418 } 419 420 if(fdef_table[func] != DC_NONE) { 421 return true; 422 } 423 return false; 424} 425 426static Cursor 427defer_cursor(int func) 428{ 429 /* Outside the table -> "No" */ 430 if(func < 0 || func >= fdef_table_max) { 431 return None; 432 } 433 434 switch(fdef_table[func]) { 435 case DC_SELECT: 436 return Scr->SelectCursor; 437 case DC_MOVE: 438 return Scr->MoveCursor; 439 case DC_DESTROY: 440 return Scr->DestroyCursor; 441 442 default: 443 /* Is there a better choice? */ 444 return None; 445 } 446 447 /* NOTREACHED */ 448 return None; 449} 450 451 452/* 453 * Checks each function in a user-defined Function list called via 454 * f.function to see any of them need to be defered. The Function config 455 * action creates pseudo-menus to store the items in that call, so we 456 * loop through the "items" in that "menu". Try not to think about that 457 * too much. 458 * 459 * This previously used a hardcoded list of functions to defer, which was 460 * substantially smaller than the list it's currently checking. It now 461 * checks all the same functions that are themselves checked 462 * individually, which is almost certainly how it should have always 463 * worked anyway. 464 */ 465static Cursor 466NeedToDefer(MenuRoot *root) 467{ 468 MenuItem *mitem; 469 470 for(mitem = root->first; mitem != NULL; mitem = mitem->next) { 471 if(should_defer(mitem->func)) { 472 Cursor dc = defer_cursor(mitem->func); 473 if(dc == None) { 474 return Scr->SelectCursor; 475 } 476 return dc; 477 } 478 } 479 return None; 480} 481 482 483 484/* 485 * Faked up handlers for functions that shouldn't ever really get to 486 * them. These are handled in various hard-coded ways before we get to 487 * automatic dispatch, so there shouldn't be any way these functions 488 * actually get called. But, just in case, return instead of dying. 489 * 490 * It's easier to just write these than to try and long-term parameterize 491 * which we expect to exist. 492 */ 493 494/* f.nop, f.title, f.separator really only exist to make lines in menus */ 495static 496DFHANDLER(nop) 497{ 498 fprintf(stderr, "%s(): Shouldn't get here.\n", __func__); 499 return; 500} 501static 502DFHANDLER(separator) 503{ 504 fprintf(stderr, "%s(): Shouldn't get here.\n", __func__); 505 return; 506} 507static 508DFHANDLER(title) 509{ 510 fprintf(stderr, "%s(): Shouldn't get here.\n", __func__); 511 return; 512} 513 514/* f.deltastop and f.function are magic */ 515static 516DFHANDLER(deltastop) 517{ 518 fprintf(stderr, "%s(): Shouldn't get here.\n", __func__); 519 return; 520} 521static 522DFHANDLER(function) 523{ 524 fprintf(stderr, "%s(): Shouldn't get here.\n", __func__); 525 return; 526} 527