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