Add reminder to investigate recursive commands for 2.6
[fvwm.git] / fvwm / misc.c
blobf303453a5f92a6f1a47a43cd1c28d375d8ad934c
1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * This module is all original code
19 * by Rob Nation
20 * Copyright 1993, Robert Nation
21 * You may use this code for any purpose, as long as the original
22 * copyright remains in the source code and all documentation
25 /* ---------------------------- included header files ---------------------- */
27 #include "config.h"
29 #include <stdio.h>
30 #include <stdarg.h>
31 #include <X11/Xatom.h>
33 #include "libs/ftime.h"
34 #ifdef FVWM_DEBUG_TIME
35 #include <sys/times.h>
36 #endif
37 #include "libs/Parse.h"
38 #include "libs/Target.h"
39 #include "fvwm.h"
40 #include "externs.h"
41 #include "execcontext.h"
42 #include "cursor.h"
43 #include "misc.h"
44 #include "screen.h"
45 #include "module_interface.h"
46 #include "events.h"
47 #include "eventmask.h"
49 /* ---------------------------- local definitions -------------------------- */
51 /* ---------------------------- local macros ------------------------------- */
53 #define GRAB_EVMASK (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | \
54 PointerMotionMask | EnterWindowMask | LeaveWindowMask)
56 /* ---------------------------- imports ------------------------------------ */
58 /* ---------------------------- included code files ------------------------ */
60 /* ---------------------------- local types -------------------------------- */
62 /* ---------------------------- forward declarations ----------------------- */
64 /* ---------------------------- local variables ---------------------------- */
66 static int grab_count[GRAB_MAXVAL] = { 1, 1, 0, 0, 0, 0, 0 };
68 /* ---------------------------- exported variables (globals) --------------- */
70 /* ---------------------------- local functions ---------------------------- */
73 * Change the appearance of the grabbed cursor.
75 static void change_grab_cursor(int cursor)
77 if (cursor != None)
79 XChangeActivePointerGrab(
80 dpy, GRAB_EVMASK, Scr.FvwmCursors[cursor], CurrentTime);
83 return;
86 /* ---------------------------- interface functions ------------------------ */
88 int GetTwoArguments(
89 char *action, int *val1, int *val2, int *val1_unit, int *val2_unit)
91 *val1_unit = Scr.MyDisplayWidth;
92 *val2_unit = Scr.MyDisplayHeight;
94 return GetTwoPercentArguments(action, val1, val2, val1_unit, val2_unit);
98 * Grab the pointer.
99 * grab_context: GRAB_NORMAL, GRAB_BUSY, GRAB_MENU, GRAB_BUSYMENU,
100 * GRAB_PASSIVE.
101 * GRAB_STARTUP and GRAB_NONE are used at startup but are not made
102 * to be grab_context.
103 * GRAB_PASSIVE does not actually grab, but only delays the following ungrab
104 * until the GRAB_PASSIVE is released too.
106 #define DEBUG_GRAB 0
107 #if DEBUG_GRAB
108 void print_grab_stats(char *text)
110 int i;
112 fprintf(stderr,"grab_stats (%s):", text);
113 for (i = 0; i < GRAB_MAXVAL; i++)
115 fprintf(stderr," %d", grab_count[i]);
117 fprintf(stderr," \n");
119 return;
121 #endif
123 Bool GrabEm(int cursor, int grab_context)
125 int i = 0;
126 int val = 0;
127 int rep;
128 Window grab_win;
129 extern Window PressedW;
131 if (grab_context <= GRAB_STARTUP || grab_context >= GRAB_MAXVAL)
133 fvwm_msg(
134 ERR, "GrabEm", "Bug: Called with illegal context %d",
135 grab_context);
136 return False;
139 if (grab_context == GRAB_PASSIVE)
141 grab_count[grab_context]++;
142 grab_count[GRAB_ALL]++;
143 return True;
146 if (grab_count[GRAB_ALL] > grab_count[GRAB_PASSIVE])
148 /* already grabbed, just change the grab cursor */
149 if (grab_context == GRAB_FREEZE_CURSOR)
151 if (XGrabPointer(
152 dpy, (PressedW != None) ?
153 PressedW : Scr.NoFocusWin, True,
154 GRAB_EVMASK, GrabModeAsync, GrabModeAsync,
155 None, Scr.FvwmCursors[CRS_DEFAULT],
156 CurrentTime) != GrabSuccess)
158 return False;
160 return True;
162 grab_count[grab_context]++;
163 grab_count[GRAB_ALL]++;
164 if (grab_context != GRAB_BUSY || grab_count[GRAB_STARTUP] == 0)
166 change_grab_cursor(cursor);
168 return True;
171 /* move the keyboard focus prior to grabbing the pointer to
172 * eliminate the enterNotify and exitNotify events that go
173 * to the windows. But GRAB_BUSY. */
174 switch (grab_context)
176 case GRAB_BUSY:
177 if ( Scr.Hilite != NULL )
179 grab_win = FW_W(Scr.Hilite);
181 else
183 grab_win = Scr.Root;
185 /* retry to grab the busy cursor only once */
186 rep = 2;
187 break;
188 case GRAB_PASSIVE:
189 /* cannot happen */
190 return False;
191 case GRAB_FREEZE_CURSOR:
192 grab_win = (PressedW != None) ? PressedW : Scr.NoFocusWin;
193 rep = 2;
194 break;
195 default:
196 grab_win = Scr.Root;
197 rep = NUMBER_OF_GRAB_ATTEMPTS;
198 break;
201 XFlush(dpy);
202 while (i < rep &&
203 (val = XGrabPointer(
204 dpy, grab_win, True, GRAB_EVMASK, GrabModeAsync,
205 GrabModeAsync, None,
206 (grab_context == GRAB_FREEZE_CURSOR) ?
207 None : Scr.FvwmCursors[cursor], CurrentTime) !=
208 GrabSuccess))
210 switch (val)
212 case GrabInvalidTime:
213 case GrabNotViewable:
214 /* give up */
215 i += rep;
216 break;
217 case GrabSuccess:
218 break;
219 case AlreadyGrabbed:
220 case GrabFrozen:
221 default:
222 /* If you go too fast, other windows may not get a
223 * chance to release any grab that they have. */
224 i++;
225 if (grab_context == GRAB_FREEZE_CURSOR)
227 break;
229 if (i < rep)
231 usleep(1000 * TIME_BETWEEN_GRAB_ATTEMPTS);
233 break;
236 XFlush(dpy);
238 /* If we fall out of the loop without grabbing the pointer, its
239 * time to give up */
240 if (val != GrabSuccess)
242 return False;
244 grab_count[grab_context]++;
245 grab_count[GRAB_ALL]++;
246 #if DEBUG_GRAB
247 print_grab_stats("grabbed");
248 #endif
249 return True;
255 * UnGrab the pointer
258 Bool UngrabEm(int ungrab_context)
260 if (ungrab_context <= GRAB_ALL || ungrab_context >= GRAB_MAXVAL)
262 fvwm_msg(
263 ERR, "UngrabEm", "Bug: Called with illegal context %d",
264 ungrab_context);
265 return False;
268 if (grab_count[ungrab_context] == 0 || grab_count[GRAB_ALL] == 0)
270 /* context is not grabbed */
271 return False;
274 XFlush(dpy);
275 grab_count[ungrab_context]--;
276 grab_count[GRAB_ALL]--;
277 if (grab_count[GRAB_ALL] > 0)
279 int new_cursor = None;
281 /* there are still grabs left - switch grab cursor */
282 switch (ungrab_context)
284 case GRAB_NORMAL:
285 case GRAB_BUSY:
286 case GRAB_MENU:
287 if (grab_count[GRAB_BUSYMENU] > 0)
289 new_cursor = CRS_WAIT;
291 else if (grab_count[GRAB_BUSY] > 0)
293 new_cursor = CRS_WAIT;
295 else if (grab_count[GRAB_MENU] > 0)
297 new_cursor = CRS_MENU;
299 else
301 new_cursor = None;
303 break;
304 case GRAB_BUSYMENU:
305 /* switch back from busymenu cursor to normal menu
306 * cursor */
307 new_cursor = CRS_MENU;
308 break;
309 default:
310 new_cursor = None;
311 break;
313 if (grab_count[GRAB_ALL] > grab_count[GRAB_PASSIVE])
315 #if DEBUG_GRAB
316 print_grab_stats("-restore");
317 #endif
318 change_grab_cursor(new_cursor);
321 else
323 #if DEBUG_GRAB
324 print_grab_stats("-ungrab");
325 #endif
326 XUngrabPointer(dpy, CurrentTime);
328 XFlush(dpy);
330 return True;
333 #ifndef fvwm_msg /* Some ports (i.e. VMS) define their own version */
335 ** fvwm_msg: used to send output from fvwm to files and or stderr/stdout
337 ** type -> DBG == Debug, ERR == Error, INFO == Information, WARN == Warning,
338 ** OLD == Command or option deprecated
339 ** id -> name of function, or other identifier
341 static char *fvwm_msg_strings[] =
343 "<<DEBUG>> ", "", "", "<<WARNING>> ", "<<DEPRECATED>> ", "<<ERROR>> "
346 void fvwm_msg(fvwm_msg_t type, char *id, char *msg, ...)
348 va_list args;
349 char fvwm_id[20];
350 char time_str[40] = "\0";
351 #ifdef FVWM_DEBUG_TIME
352 clock_t time_val, time_taken;
353 static clock_t start_time = 0;
354 static clock_t prev_time = 0;
355 struct tms not_used_tms;
356 time_t mytime;
357 struct tm *t_ptr;
358 #endif
360 #ifdef FVWM_DEBUG_TIME
361 time(&mytime);
362 t_ptr = localtime(&mytime);
363 if (start_time == 0)
365 /* get clock ticks */
366 prev_time = start_time = (unsigned int)times(&not_used_tms);
368 time_val = (unsigned int)times(&not_used_tms); /* get clock ticks */
369 time_taken = time_val - prev_time;
370 prev_time = time_val;
371 sprintf(time_str, "%.2d:%.2d:%.2d%7ld ",
372 t_ptr->tm_hour, t_ptr->tm_min, t_ptr->tm_sec, time_taken);
373 #endif
375 strcpy(fvwm_id, "fvwm");
376 if (Scr.NumberOfScreens > 1)
378 sprintf(&fvwm_id[strlen(fvwm_id)], ".%d", (int)Scr.screen);
381 fprintf(stderr, "%s[%s][%s]: %s",
382 time_str, fvwm_id, id, fvwm_msg_strings[(int)type]);
384 if (type == ECHO)
386 /* user echos must be printed as a literal string */
387 fprintf(stderr, "%s", msg);
389 else
391 va_start(args, msg);
392 vfprintf(stderr, msg, args);
393 va_end(args);
395 fprintf(stderr, "\n");
397 if (type == ERR)
399 /* I hate to use a fixed length but this will do for now */
400 char tmp[2 * MAX_TOKEN_LENGTH];
401 sprintf(tmp, "[%s][%s]: %s",
402 fvwm_id, id, fvwm_msg_strings[(int)type]);
403 va_start(args, msg);
404 vsprintf(tmp + strlen(tmp), msg, args);
405 va_end(args);
406 tmp[strlen(tmp) + 1] = '\0';
407 tmp[strlen(tmp)] = '\n';
408 if (strlen(tmp) >= MAX_MODULE_INPUT_TEXT_LEN)
410 sprintf(tmp + MAX_MODULE_INPUT_TEXT_LEN - 5, "...\n");
412 BroadcastName(M_ERROR, 0, 0, 0, tmp);
415 } /* fvwm_msg */
416 #endif
418 void fvwm_msg_report_app(void)
420 fprintf(
421 stderr,
422 " If you are having a problem with the application, send a"
423 " bug report\n"
424 " with this message included to the application owner.\n"
425 " There is no need to notify fvwm-workers@fvwm.org.\n");
427 return;
430 void fvwm_msg_report_app_and_workers(void)
432 fprintf(
433 stderr,
434 " If you are having a problem with the application, send"
435 " a bug report with\n"
436 " this message included to the application owner and"
437 " notify\n"
438 " fvwm-workers@fvwm.org.\n");
440 return;
443 /* Store the last item that was added with '+' */
444 void set_last_added_item(last_added_item_t type, void *item)
446 Scr.last_added_item.type = type;
447 Scr.last_added_item.item = item;
449 return;
452 /* some fancy font handling stuff */
453 void NewFontAndColor(FlocaleFont *flf, Pixel color, Pixel backcolor)
455 Globalgcm = GCForeground | GCBackground;
456 if (flf->font)
458 Globalgcm |= GCFont;
459 Globalgcv.font = flf->font->fid;
461 Globalgcv.foreground = color;
462 Globalgcv.background = backcolor;
463 XChangeGC(dpy,Scr.TitleGC,Globalgcm,&Globalgcv);
465 return;
471 * For menus, move, and resize operations, we can effect keyboard
472 * shortcuts by warping the pointer.
475 void Keyboard_shortcuts(
476 XEvent *ev, FvwmWindow *fw, int *x_defect, int *y_defect,
477 int ReturnEvent)
479 int x_move_size = 0;
480 int y_move_size = 0;
482 if (fw)
484 x_move_size = fw->hints.width_inc;
485 y_move_size = fw->hints.height_inc;
487 fvwmlib_keyboard_shortcuts(
488 dpy, Scr.screen, ev, x_move_size, y_move_size, x_defect,
489 y_defect, ReturnEvent);
491 return;
497 * Check if the given FvwmWindow structure still points to a valid window.
501 Bool check_if_fvwm_window_exists(FvwmWindow *fw)
503 FvwmWindow *t;
505 for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
507 if (t == fw)
508 return True;
510 return False;
513 /* rounds x down to the next multiple of m */
514 int truncate_to_multiple (int x, int m)
516 return (x < 0) ? (m * (((x + 1) / m) - 1)) : (m * (x / m));
519 Bool IsRectangleOnThisPage(const rectangle *rec, int desk)
521 return (desk == Scr.CurrentDesk &&
522 rec->x + (signed int)rec->width > 0 &&
523 (rec->x < 0 || rec->x < Scr.MyDisplayWidth) &&
524 rec->y + (signed int)rec->height > 0 &&
525 (rec->y < 0 || rec->y < Scr.MyDisplayHeight)) ?
526 True : False;
529 /* returns the FvwmWindow that contains the pointer or NULL if none */
530 FvwmWindow *get_pointer_fvwm_window(void)
532 int x,y;
533 Window win;
534 Window ancestor;
535 FvwmWindow *t;
537 if (FQueryPointer(
538 dpy, Scr.Root, &JunkRoot, &win, &JunkX, &JunkY,
539 &x, &y, &JunkMask) == False)
541 /* pointer is on a different screen */
542 return NULL;
544 for (t = NULL ; win != Scr.Root && win != None; win = ancestor)
546 Window root = None;
547 Window *children;
548 unsigned int nchildren;
550 if (XFindContext(dpy, win, FvwmContext, (caddr_t *) &t) !=
551 XCNOENT)
553 /* found a matching window context */
554 return t;
556 /* get next higher ancestor window */
557 children = NULL;
558 if (!XQueryTree(
559 dpy, win, &root, &ancestor, &children, &nchildren))
561 return NULL;
563 if (children)
565 XFree(children);
569 return t;
573 /* Returns the current X server time */
574 Time get_server_time(void)
576 XEvent xev;
577 XSetWindowAttributes attr;
579 /* add PropChange to NoFocusWin events */
580 attr.event_mask = PropertyChangeMask;
581 XChangeWindowAttributes (dpy, Scr.NoFocusWin, CWEventMask, &attr);
582 /* provoke an event */
583 XChangeProperty(
584 dpy, Scr.NoFocusWin, XA_WM_CLASS, XA_STRING, 8, PropModeAppend,
585 NULL, 0);
586 FWindowEvent(dpy, Scr.NoFocusWin, PropertyChangeMask, &xev);
587 attr.event_mask = XEVMASK_NOFOCUSW;
588 XChangeWindowAttributes(dpy, Scr.NoFocusWin, CWEventMask, &attr);
590 return xev.xproperty.time;
593 void print_g(char *text, rectangle *g)
595 fprintf(stderr,"%s: ", (text != NULL) ? text : "");
596 if (g == NULL)
598 fprintf(stderr, "(null)\n");
600 return;
602 fprintf(stderr,"%4d %4d %4dx%4d (%4d - %4d, %4d - %4d)\n",
603 g->x, g->y, g->width, g->height,
604 g->x, g->x + g->width, g->y, g->y + g->height);
606 return;