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