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.
6 *
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.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, see: <http://www.gnu.org/licenses/>
14 */
15
16 /* ---------------------------- included header files ---------------------- */
17
18 #include "config.h"
19
20 #include <stdio.h>
21 #include <stdarg.h>
22 #include <X11/Xatom.h>
23
24 #include "libs/ftime.h"
25 #ifdef FVWM_DEBUG_TIME
26 #include <sys/times.h>
27 #endif
28 #include "libs/Parse.h"
29 #include "libs/Target.h"
30 #include "fvwm.h"
31 #include "externs.h"
32 #include "execcontext.h"
33 #include "cursor.h"
34 #include "misc.h"
35 #include "screen.h"
36 #include "module_interface.h"
37 #include "events.h"
38 #include "eventmask.h"
39
40 /* ---------------------------- local definitions -------------------------- */
41
42 /* ---------------------------- local macros ------------------------------- */
43
44 #define GRAB_EVMASK (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | \
45 PointerMotionMask | EnterWindowMask | LeaveWindowMask)
46
47 /* ---------------------------- imports ------------------------------------ */
48
49 /* ---------------------------- included code files ------------------------ */
50
51 /* ---------------------------- local types -------------------------------- */
52
53 /* ---------------------------- forward declarations ----------------------- */
54
55 /* ---------------------------- local variables ---------------------------- */
56
57 static int grab_count[GRAB_MAXVAL] = { 1, 1, 0, 0, 0, 0, 0 };
58
59 /* ---------------------------- exported variables (globals) --------------- */
60
61 /* ---------------------------- local functions ---------------------------- */
62
63 /*
64 * Change the appearance of the grabbed cursor.
65 */
change_grab_cursor(int cursor)66 static void change_grab_cursor(int cursor)
67 {
68 if (cursor != None)
69 {
70 XChangeActivePointerGrab(
71 dpy, GRAB_EVMASK, Scr.FvwmCursors[cursor], CurrentTime);
72 }
73
74 return;
75 }
76
77 /* ---------------------------- interface functions ------------------------ */
78
GetTwoArguments(char * action,int * val1,int * val2,int * val1_unit,int * val2_unit)79 int GetTwoArguments(
80 char *action, int *val1, int *val2, int *val1_unit, int *val2_unit)
81 {
82 *val1_unit = Scr.MyDisplayWidth;
83 *val2_unit = Scr.MyDisplayHeight;
84
85 return GetTwoPercentArguments(action, val1, val2, val1_unit, val2_unit);
86 }
87
88 /*
89 * Grab the pointer.
90 * grab_context: GRAB_NORMAL, GRAB_BUSY, GRAB_MENU, GRAB_BUSYMENU,
91 * GRAB_PASSIVE.
92 * GRAB_STARTUP and GRAB_NONE are used at startup but are not made
93 * to be grab_context.
94 * GRAB_PASSIVE does not actually grab, but only delays the following ungrab
95 * until the GRAB_PASSIVE is released too.
96 */
97 #define DEBUG_GRAB 0
98 #if DEBUG_GRAB
print_grab_stats(char * text)99 void print_grab_stats(char *text)
100 {
101 int i;
102
103 fprintf(stderr,"grab_stats (%s):", text);
104 for (i = 0; i < GRAB_MAXVAL; i++)
105 {
106 fprintf(stderr," %d", grab_count[i]);
107 }
108 fprintf(stderr," \n");
109
110 return;
111 }
112 #endif
113
GrabEm(int cursor,int grab_context)114 Bool GrabEm(int cursor, int grab_context)
115 {
116 int i = 0;
117 int val = 0;
118 int rep;
119 Window grab_win;
120 extern Window PressedW;
121
122 if (grab_context <= GRAB_STARTUP || grab_context >= GRAB_MAXVAL)
123 {
124 fvwm_msg(
125 ERR, "GrabEm", "Bug: Called with illegal context %d",
126 grab_context);
127 return False;
128 }
129
130 if (grab_context == GRAB_PASSIVE)
131 {
132 grab_count[grab_context]++;
133 grab_count[GRAB_ALL]++;
134 return True;
135 }
136
137 if (grab_count[GRAB_ALL] > grab_count[GRAB_PASSIVE])
138 {
139 /* already grabbed, just change the grab cursor */
140 if (grab_context == GRAB_FREEZE_CURSOR)
141 {
142 if (XGrabPointer(
143 dpy, (PressedW != None) ?
144 PressedW : Scr.NoFocusWin, True,
145 GRAB_EVMASK, GrabModeAsync, GrabModeAsync,
146 None, Scr.FvwmCursors[CRS_DEFAULT],
147 CurrentTime) != GrabSuccess)
148 {
149 return False;
150 }
151 return True;
152 }
153 grab_count[grab_context]++;
154 grab_count[GRAB_ALL]++;
155 if (grab_context != GRAB_BUSY || grab_count[GRAB_STARTUP] == 0)
156 {
157 change_grab_cursor(cursor);
158 }
159 return True;
160 }
161
162 /* move the keyboard focus prior to grabbing the pointer to
163 * eliminate the enterNotify and exitNotify events that go
164 * to the windows. But GRAB_BUSY. */
165 switch (grab_context)
166 {
167 case GRAB_BUSY:
168 if ( Scr.Hilite != NULL )
169 {
170 grab_win = FW_W(Scr.Hilite);
171 }
172 else
173 {
174 grab_win = Scr.Root;
175 }
176 /* retry to grab the busy cursor only once */
177 rep = 2;
178 break;
179 case GRAB_PASSIVE:
180 /* cannot happen */
181 return False;
182 case GRAB_FREEZE_CURSOR:
183 grab_win = (PressedW != None) ? PressedW : Scr.NoFocusWin;
184 rep = 2;
185 break;
186 default:
187 grab_win = Scr.Root;
188 rep = NUMBER_OF_GRAB_ATTEMPTS;
189 break;
190 }
191
192 XFlush(dpy);
193 while (i < rep &&
194 (val = XGrabPointer(
195 dpy, grab_win, True, GRAB_EVMASK, GrabModeAsync,
196 GrabModeAsync, None,
197 (grab_context == GRAB_FREEZE_CURSOR) ?
198 None : Scr.FvwmCursors[cursor], CurrentTime) !=
199 GrabSuccess))
200 {
201 switch (val)
202 {
203 case GrabInvalidTime:
204 case GrabNotViewable:
205 /* give up */
206 i += rep;
207 break;
208 case GrabSuccess:
209 break;
210 case AlreadyGrabbed:
211 case GrabFrozen:
212 default:
213 /* If you go too fast, other windows may not get a
214 * chance to release any grab that they have. */
215 i++;
216 if (grab_context == GRAB_FREEZE_CURSOR)
217 {
218 break;
219 }
220 if (i < rep)
221 {
222 usleep(1000 * TIME_BETWEEN_GRAB_ATTEMPTS);
223 }
224 break;
225 }
226 }
227 XFlush(dpy);
228
229 /* If we fall out of the loop without grabbing the pointer, its
230 * time to give up */
231 if (val != GrabSuccess)
232 {
233 return False;
234 }
235 grab_count[grab_context]++;
236 grab_count[GRAB_ALL]++;
237 #if DEBUG_GRAB
238 print_grab_stats("grabbed");
239 #endif
240 return True;
241 }
242
243
244 /*
245 *
246 * UnGrab the pointer
247 *
248 */
UngrabEm(int ungrab_context)249 Bool UngrabEm(int ungrab_context)
250 {
251 if (ungrab_context <= GRAB_ALL || ungrab_context >= GRAB_MAXVAL)
252 {
253 fvwm_msg(
254 ERR, "UngrabEm", "Bug: Called with illegal context %d",
255 ungrab_context);
256 return False;
257 }
258
259 if (grab_count[ungrab_context] == 0 || grab_count[GRAB_ALL] == 0)
260 {
261 /* context is not grabbed */
262 return False;
263 }
264
265 XFlush(dpy);
266 grab_count[ungrab_context]--;
267 grab_count[GRAB_ALL]--;
268 if (grab_count[GRAB_ALL] > 0)
269 {
270 int new_cursor = None;
271
272 /* there are still grabs left - switch grab cursor */
273 switch (ungrab_context)
274 {
275 case GRAB_NORMAL:
276 case GRAB_BUSY:
277 case GRAB_MENU:
278 if (grab_count[GRAB_BUSYMENU] > 0)
279 {
280 new_cursor = CRS_WAIT;
281 }
282 else if (grab_count[GRAB_BUSY] > 0)
283 {
284 new_cursor = CRS_WAIT;
285 }
286 else if (grab_count[GRAB_MENU] > 0)
287 {
288 new_cursor = CRS_MENU;
289 }
290 else
291 {
292 new_cursor = None;
293 }
294 break;
295 case GRAB_BUSYMENU:
296 /* switch back from busymenu cursor to normal menu
297 * cursor */
298 new_cursor = CRS_MENU;
299 break;
300 default:
301 new_cursor = None;
302 break;
303 }
304 if (grab_count[GRAB_ALL] > grab_count[GRAB_PASSIVE])
305 {
306 #if DEBUG_GRAB
307 print_grab_stats("-restore");
308 #endif
309 change_grab_cursor(new_cursor);
310 }
311 }
312 else
313 {
314 #if DEBUG_GRAB
315 print_grab_stats("-ungrab");
316 #endif
317 XUngrabPointer(dpy, CurrentTime);
318 }
319 XFlush(dpy);
320
321 return True;
322 }
323
324 #ifndef fvwm_msg /* Some ports (i.e. VMS) define their own version */
325 /*
326 ** fvwm_msg: used to send output from fvwm to files and or stderr/stdout
327 **
328 ** type -> DBG == Debug, ERR == Error, INFO == Information, WARN == Warning,
329 ** OLD == Command or option deprecated
330 ** id -> name of function, or other identifier
331 */
332 static char *fvwm_msg_strings[] =
333 {
334 "<<DEBUG>> ", "", "", "<<WARNING>> ", "<<DEPRECATED>> ", "<<ERROR>> "
335 };
336
fvwm_msg(fvwm_msg_t type,char * id,char * msg,...)337 void fvwm_msg(fvwm_msg_t type, char *id, char *msg, ...)
338 {
339 va_list args;
340 char *mfmt;
341 char fvwm_id[20];
342 char time_str[40] = "\0";
343 #ifdef FVWM_DEBUG_TIME
344 clock_t time_val, time_taken;
345 static clock_t start_time = 0;
346 static clock_t prev_time = 0;
347 struct tms not_used_tms;
348 time_t mytime;
349 struct tm *t_ptr;
350 #endif
351
352 #ifdef FVWM_DEBUG_TIME
353 time(&mytime);
354 t_ptr = localtime(&mytime);
355 if (start_time == 0)
356 {
357 /* get clock ticks */
358 prev_time = start_time = (unsigned int)times(¬_used_tms);
359 }
360 time_val = (unsigned int)times(¬_used_tms); /* get clock ticks */
361 time_taken = time_val - prev_time;
362 prev_time = time_val;
363 sprintf(time_str, "%.2d:%.2d:%.2d%7ld ",
364 t_ptr->tm_hour, t_ptr->tm_min, t_ptr->tm_sec, time_taken);
365 #endif
366
367 strcpy(fvwm_id, "fvwm");
368 if (Scr.NumberOfScreens > 1)
369 {
370 sprintf(&fvwm_id[strlen(fvwm_id)], ".%d", (int)Scr.screen);
371 }
372
373 if (type == ERR)
374 {
375 /* I hate to use a fixed length but this will do for now */
376 char tmp[2 * MAX_TOKEN_LENGTH];
377 sprintf(tmp, "[%s][%s]: %s",
378 fvwm_id, id, fvwm_msg_strings[(int)type]);
379 va_start(args, msg);
380 vsprintf(tmp + strlen(tmp), msg, args);
381 va_end(args);
382 tmp[strlen(tmp) + 1] = '\0';
383 tmp[strlen(tmp)] = '\n';
384 if (strlen(tmp) >= MAX_MODULE_INPUT_TEXT_LEN)
385 {
386 sprintf(tmp + MAX_MODULE_INPUT_TEXT_LEN - 5, "...\n");
387 }
388 fprintf(stderr, "%s", tmp);
389 BroadcastName(M_ERROR, 0, 0, 0, tmp);
390 }
391 else
392 {
393 fprintf(stderr, "%s[%s][%s]: %s",
394 time_str, fvwm_id, id, fvwm_msg_strings[(int)type]);
395
396 va_start(args, msg);
397 {
398 int n;
399
400 n = asprintf(&mfmt, "%s\n", msg);
401 (void)n;
402 }
403 vfprintf(stderr, mfmt, args);
404 va_end(args);
405 free(mfmt);
406 }
407
408 } /* fvwm_msg */
409 #endif
410
fvwm_msg_report_app(void)411 void fvwm_msg_report_app(void)
412 {
413 fprintf(
414 stderr,
415 " If you are having a problem with the application, send a"
416 " bug report\n"
417 " with this message included to the application owner.\n"
418 " There is no need to notify fvwm-workers@fvwm.org.\n");
419
420 return;
421 }
422
fvwm_msg_report_app_and_workers(void)423 void fvwm_msg_report_app_and_workers(void)
424 {
425 fprintf(
426 stderr,
427 " If you are having a problem with the application, send"
428 " a bug report with\n"
429 " this message included to the application owner and"
430 " notify\n"
431 " fvwm-workers@fvwm.org.\n");
432
433 return;
434 }
435
436 /* Store the last item that was added with '+' */
set_last_added_item(last_added_item_t type,void * item)437 void set_last_added_item(last_added_item_t type, void *item)
438 {
439 Scr.last_added_item.type = type;
440 Scr.last_added_item.item = item;
441
442 return;
443 }
444
445 /* some fancy font handling stuff */
NewFontAndColor(FlocaleFont * flf,Pixel color,Pixel backcolor)446 void NewFontAndColor(FlocaleFont *flf, Pixel color, Pixel backcolor)
447 {
448 Globalgcm = GCForeground | GCBackground;
449 if (flf->font)
450 {
451 Globalgcm |= GCFont;
452 Globalgcv.font = flf->font->fid;
453 }
454 Globalgcv.foreground = color;
455 Globalgcv.background = backcolor;
456 XChangeGC(dpy,Scr.TitleGC,Globalgcm,&Globalgcv);
457
458 return;
459 }
460
461
462 /*
463 *
464 * For menus, move, and resize operations, we can effect keyboard
465 * shortcuts by warping the pointer.
466 *
467 */
Keyboard_shortcuts(XEvent * ev,FvwmWindow * fw,int * x_defect,int * y_defect,int ReturnEvent)468 void Keyboard_shortcuts(
469 XEvent *ev, FvwmWindow *fw, int *x_defect, int *y_defect,
470 int ReturnEvent)
471 {
472 int x_move_size = 0;
473 int y_move_size = 0;
474
475 if (fw)
476 {
477 x_move_size = fw->hints.width_inc;
478 y_move_size = fw->hints.height_inc;
479 }
480 fvwmlib_keyboard_shortcuts(
481 dpy, Scr.screen, ev, x_move_size, y_move_size, x_defect,
482 y_defect, ReturnEvent);
483
484 return;
485 }
486
487
488 /*
489 *
490 * Check if the given FvwmWindow structure still points to a valid window.
491 *
492 */
493
check_if_fvwm_window_exists(FvwmWindow * fw)494 Bool check_if_fvwm_window_exists(FvwmWindow *fw)
495 {
496 FvwmWindow *t;
497
498 for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
499 {
500 if (t == fw)
501 return True;
502 }
503 return False;
504 }
505
506 /* rounds x down to the next multiple of m */
truncate_to_multiple(int x,int m)507 int truncate_to_multiple (int x, int m)
508 {
509 return (x < 0) ? (m * (((x + 1) / m) - 1)) : (m * (x / m));
510 }
511
IsRectangleOnThisPage(const rectangle * rec,int desk)512 Bool IsRectangleOnThisPage(const rectangle *rec, int desk)
513 {
514 return (desk == Scr.CurrentDesk &&
515 rec->x + (signed int)rec->width > 0 &&
516 (rec->x < 0 || rec->x < Scr.MyDisplayWidth) &&
517 rec->y + (signed int)rec->height > 0 &&
518 (rec->y < 0 || rec->y < Scr.MyDisplayHeight)) ?
519 True : False;
520 }
521
522 /* returns the FvwmWindow that contains the pointer or NULL if none */
get_pointer_fvwm_window(void)523 FvwmWindow *get_pointer_fvwm_window(void)
524 {
525 int x,y;
526 Window win;
527 Window ancestor;
528 FvwmWindow *t;
529
530 if (FQueryPointer(
531 dpy, Scr.Root, &JunkRoot, &win, &JunkX, &JunkY,
532 &x, &y, &JunkMask) == False)
533 {
534 /* pointer is on a different screen */
535 return NULL;
536 }
537 for (t = NULL ; win != Scr.Root && win != None; win = ancestor)
538 {
539 Window root = None;
540 Window *children;
541 unsigned int nchildren;
542
543 if (XFindContext(dpy, win, FvwmContext, (caddr_t *) &t) !=
544 XCNOENT)
545 {
546 /* found a matching window context */
547 return t;
548 }
549 /* get next higher ancestor window */
550 children = NULL;
551 if (!XQueryTree(
552 dpy, win, &root, &ancestor, &children, &nchildren))
553 {
554 return NULL;
555 }
556 if (children)
557 {
558 XFree(children);
559 }
560 }
561
562 return t;
563 }
564
565
566 /* Returns the current X server time */
get_server_time(void)567 Time get_server_time(void)
568 {
569 XEvent xev;
570 XSetWindowAttributes attr;
571
572 /* add PropChange to NoFocusWin events */
573 attr.event_mask = PropertyChangeMask;
574 XChangeWindowAttributes (dpy, Scr.NoFocusWin, CWEventMask, &attr);
575 /* provoke an event */
576 XChangeProperty(
577 dpy, Scr.NoFocusWin, XA_WM_CLASS, XA_STRING, 8, PropModeAppend,
578 NULL, 0);
579 FWindowEvent(dpy, Scr.NoFocusWin, PropertyChangeMask, &xev);
580 attr.event_mask = XEVMASK_NOFOCUSW;
581 XChangeWindowAttributes(dpy, Scr.NoFocusWin, CWEventMask, &attr);
582
583 return xev.xproperty.time;
584 }
585
print_g(char * text,rectangle * g)586 void print_g(char *text, rectangle *g)
587 {
588 fprintf(stderr,"%s: ", (text != NULL) ? text : "");
589 if (g == NULL)
590 {
591 fprintf(stderr, "(null)\n");
592
593 return;
594 }
595 fprintf(stderr,"%4d %4d %4dx%4d (%4d - %4d, %4d - %4d)\n",
596 g->x, g->y, g->width, g->height,
597 g->x, g->x + g->width, g->y, g->y + g->height);
598
599 return;
600 }
601