functions.c revision 0bbfda8a
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 if((context == C_ROOT) || (context == C_ALTERNATE)) { 370 LastCursor = cursor; 371 XGrabPointer(dpy, 372 Scr->Root, 373 True, 374 ButtonPressMask | ButtonReleaseMask, 375 GrabModeAsync, 376 GrabModeAsync, 377 (func == F_ADOPTWINDOW) ? None : Scr->Root, 378 cursor, 379 CurrentTime); 380 RootFunction = func; 381 382 return true; 383 } 384 385 return false; 386} 387 388 389/* 390 * Various determinates of whether a function should be deferred if its 391 * called in a general (rather than win-specific) context, and what 392 * cursor should be used in the meantime. 393 * 394 * We define a big lookup array to do it. We have to indirect through an 395 * intermediate enum value instead of just the cursor since it isn't 396 * available at compile time, and we can't just make it a pointer into 397 * Scr since there are [potentially] multiple Scr's anyway. And we need 398 * an explicit unused DC_NONE value so our real values are all non-zero; 399 * the ones we don't explicitly set get initialized to 0, which we can 400 * then take as a flag saying "we don't defer this func". 401 * 402 * fdef_table in functions_deferral.h generated from functions_defs.list. 403 */ 404 405static bool 406should_defer(int func) 407{ 408 /* Outside the table -> "No" */ 409 if(func < 0 || func >= fdef_table_max) { 410 return false; 411 } 412 413 if(fdef_table[func] != DC_NONE) { 414 return true; 415 } 416 return false; 417} 418 419static Cursor 420defer_cursor(int func) 421{ 422 /* Outside the table -> "No" */ 423 if(func < 0 || func >= fdef_table_max) { 424 return None; 425 } 426 427 switch(fdef_table[func]) { 428 case DC_SELECT: 429 return Scr->SelectCursor; 430 case DC_MOVE: 431 return Scr->MoveCursor; 432 case DC_DESTROY: 433 return Scr->DestroyCursor; 434 435 default: 436 /* Is there a better choice? */ 437 return None; 438 } 439 440 /* NOTREACHED */ 441 return None; 442} 443 444 445/* 446 * Checks each function in a user-defined Function list called via 447 * f.function to see any of them need to be defered. The Function config 448 * action creates pseudo-menus to store the items in that call, so we 449 * loop through the "items" in that "menu". Try not to think about that 450 * too much. 451 * 452 * This previously used a hardcoded list of functions to defer, which was 453 * substantially smaller than the list it's currently checking. It now 454 * checks all the same functions that are themselves checked 455 * individually, which is almost certainly how it should have always 456 * worked anyway. 457 */ 458static Cursor 459NeedToDefer(MenuRoot *root) 460{ 461 MenuItem *mitem; 462 463 for(mitem = root->first; mitem != NULL; mitem = mitem->next) { 464 if(should_defer(mitem->func)) { 465 Cursor dc = defer_cursor(mitem->func); 466 if(dc == None) { 467 return Scr->SelectCursor; 468 } 469 return dc; 470 } 471 } 472 return None; 473} 474 475 476 477/* 478 * Faked up handlers for functions that shouldn't ever really get to 479 * them. These are handled in various hard-coded ways before we get to 480 * automatic dispatch, so there shouldn't be any way these functions 481 * actually get called. But, just in case, return instead of dying. 482 * 483 * It's easier to just write these than to try and long-term parameterize 484 * which we expect to exist. 485 */ 486 487/* f.nop, f.title, f.separator really only exist to make lines in menus */ 488static 489DFHANDLER(nop) 490{ 491 fprintf(stderr, "%s(): Shouldn't get here.\n", __func__); 492 return; 493} 494static 495DFHANDLER(separator) 496{ 497 fprintf(stderr, "%s(): Shouldn't get here.\n", __func__); 498 return; 499} 500static 501DFHANDLER(title) 502{ 503 fprintf(stderr, "%s(): Shouldn't get here.\n", __func__); 504 return; 505} 506 507/* f.deltastop and f.function are magic */ 508static 509DFHANDLER(deltastop) 510{ 511 fprintf(stderr, "%s(): Shouldn't get here.\n", __func__); 512 return; 513} 514static 515DFHANDLER(function) 516{ 517 fprintf(stderr, "%s(): Shouldn't get here.\n", __func__); 518 return; 519} 520