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
18 static DFHANDLER(nop);
19 static DFHANDLER(separator);
20 static DFHANDLER(title);
21 static DFHANDLER(deltastop);
22 static 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 */
45 int RootFunction = 0;
46
47
48 /*
49 * Track whether a window gets moved by move operations: used for
50 * f.deltastop handling.
51 */
52 bool 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 */
65 bool func_reset_cursor;
66
67 /*
68 * Time of various actions: used in ConstrainedMoveTime related bits in
69 * some window moving/resizing.
70 */
71 Time last_time = 0;
72
73
74 static bool EF_main(EF_FULLPROTO);
75
76 static bool DeferExecution(int context, int func, Cursor cursor);
77 static bool should_defer(int func);
78 static Cursor defer_cursor(int func);
79 static 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 */
98 void
ExecuteFunction(EF_FULLPROTO)99 ExecuteFunction(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 */
114 static bool
EF_main(EF_FULLPROTO)115 EF_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 */
342 static Cursor LastCursor;
343
344 void
ReGrab(void)345 ReGrab(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 */
366 static bool
DeferExecution(int context,int func,Cursor cursor)367 DeferExecution(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
405 static bool
should_defer(int func)406 should_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
419 static Cursor
defer_cursor(int func)420 defer_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 */
458 static Cursor
NeedToDefer(MenuRoot * root)459 NeedToDefer(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 */
488 static
DFHANDLER(nop)489 DFHANDLER(nop)
490 {
491 fprintf(stderr, "%s(): Shouldn't get here.\n", __func__);
492 return;
493 }
494 static
DFHANDLER(separator)495 DFHANDLER(separator)
496 {
497 fprintf(stderr, "%s(): Shouldn't get here.\n", __func__);
498 return;
499 }
500 static
DFHANDLER(title)501 DFHANDLER(title)
502 {
503 fprintf(stderr, "%s(): Shouldn't get here.\n", __func__);
504 return;
505 }
506
507 /* f.deltastop and f.function are magic */
508 static
DFHANDLER(deltastop)509 DFHANDLER(deltastop)
510 {
511 fprintf(stderr, "%s(): Shouldn't get here.\n", __func__);
512 return;
513 }
514 static
DFHANDLER(function)515 DFHANDLER(function)
516 {
517 fprintf(stderr, "%s(): Shouldn't get here.\n", __func__);
518 return;
519 }
520