73
83
fake_event.xany.display = si->dpy;
74
84
fake_event.xany.window = 0;
75
85
XPutBackEvent (si->dpy, &fake_event);
87
/* If we are the timer that just went off, clear the pointer to the id. */
90
if (si->timer_id && *id != si->timer_id)
91
abort(); /* oops, scheduled timer twice?? */
80
98
schedule_wakeup_event (saver_info *si, Time when, Bool verbose_p)
103
fprintf (stderr, "%s: idle_timer already running\n", blurb());
82
107
/* Wake up periodically to ask the server if we are idle. */
83
108
si->timer_id = XtAppAddTimeOut (si->app, when, idle_timer,
88
112
fprintf (stderr, "%s: starting idle_timer (%ld, %ld)\n",
89
113
blurb(), when, si->timer_id);
90
#endif /* DEBUG_TIMERS */
125
154
the mouse or touching the keyboard, we won't know that they've been
126
155
active, and the screensaver will come on. That sucks, but I don't
127
156
know how to get around it.
158
Since X presents mouse wheels as clicks, this applies to those, too:
159
scrolling through a document using only the mouse wheel doesn't
160
count as activity... Fortunately, /proc/interrupts helps, on
161
systems that have it. Oh, if it's a PS/2 mouse, not serial or USB.
129
164
XSelectInput (si->dpy, window, SubstructureNotifyMask | events);
131
if (top_p && p->verbose_p && (events & KeyPressMask))
166
if (top_p && p->debug_p && (events & KeyPressMask))
133
168
/* Only mention one window per tree (hack hack). */
134
fprintf (stderr, "%s: selected KeyPress on 0x%lX\n", blurb(),
135
(unsigned long) window);
169
fprintf (stderr, "%s: %d: selected KeyPress on 0x%lX\n",
170
blurb(), screen_no, (unsigned long) window);
248
283
si->cycle_id = XtAppAddTimeOut (si->app, how_long, cycle_timer,
253
287
fprintf (stderr, "%s: starting cycle_timer (%ld, %ld)\n",
254
288
blurb(), how_long, si->cycle_id);
255
# endif /* DEBUG_TIMERS */
261
fprintf (stderr, "%s: not starting cycle_timer: how_long == %d\n",
293
fprintf (stderr, "%s: not starting cycle_timer: how_long == %ld\n",
294
blurb(), (unsigned long) how_long);
264
# endif /* DEBUG_TIMERS */
337
/* Returns true if the mouse has moved since the last time we checked.
338
Small motions (of less than "hysteresis" pixels/second) are ignored.
341
pointer_moved_p (saver_screen_info *ssi, Bool mods_p)
343
saver_info *si = ssi->global;
344
saver_preferences *p = &si->prefs;
347
int root_x, root_y, x, y;
349
time_t now = time ((time_t *) 0);
350
unsigned int distance, dps;
351
unsigned long seconds = 0;
352
Bool moved_p = False;
354
/* don't check xinerama pseudo-screens. */
355
if (!ssi->real_screen_p) return False;
357
if (!XQueryPointer (si->dpy, ssi->screensaver_window, &root, &child,
358
&root_x, &root_y, &x, &y, &mask))
360
/* If XQueryPointer() returns false, the mouse is not on this screen.
368
distance = MAX (ABS (ssi->poll_mouse_last_root_x - root_x),
369
ABS (ssi->poll_mouse_last_root_y - root_y));
370
seconds = (now - ssi->poll_mouse_last_time);
373
/* When the screen is blanked, we get MotionNotify events, but when not
374
blanked, we poll only every 5 seconds, and that's not enough resolution
375
to do hysteresis based on a 1 second interval. So, assume that any
376
motion we've seen during the 5 seconds when our eyes were closed happened
377
in the last 1 second instead.
379
if (seconds > 1) seconds = 1;
381
dps = (seconds <= 0 ? distance : (distance / seconds));
383
/* Motion only counts if the rate is more than N pixels per second.
385
if (dps >= p->pointer_hysteresis &&
389
if (ssi->poll_mouse_last_root_x == -1 ||
390
ssi->poll_mouse_last_root_y == -1 ||
395
if (p->debug_p && (distance != 0 || moved_p))
397
fprintf (stderr, "%s: %d: pointer %s", blurb(), ssi->number,
398
(moved_p ? "moved: " : "ignored:"));
399
if (ssi->poll_mouse_last_root_x == -1)
400
fprintf (stderr, "off screen");
402
fprintf (stderr, "%d,%d",
403
ssi->poll_mouse_last_root_x,
404
ssi->poll_mouse_last_root_y);
405
fprintf (stderr, " -> ");
407
fprintf (stderr, "off screen.");
409
fprintf (stderr, "%d,%d", root_x, root_y);
410
if (ssi->poll_mouse_last_root_x != -1 && root_x != -1)
411
fprintf (stderr, " (%d,%d; %d/%lu=%d)",
412
ABS(ssi->poll_mouse_last_root_x - root_x),
413
ABS(ssi->poll_mouse_last_root_y - root_y),
414
distance, seconds, dps);
416
fprintf (stderr, ".\n");
421
mask != ssi->poll_mouse_last_mask)
426
fprintf (stderr, "%s: %d: modifiers changed: 0x%04x -> 0x%04x.\n",
427
blurb(), ssi->number, ssi->poll_mouse_last_mask, mask);
430
si->last_activity_screen = ssi;
431
ssi->poll_mouse_last_child = child;
432
ssi->poll_mouse_last_mask = mask;
434
if (moved_p || seconds > 0)
436
ssi->poll_mouse_last_time = now;
437
ssi->poll_mouse_last_root_x = root_x;
438
ssi->poll_mouse_last_root_y = root_y;
307
445
/* When we aren't using a server extension, this timer is used to periodically
308
446
wake up and poll the mouse position, which is possibly more reliable than
309
447
selecting motion events on every window.
328
si->check_pointer_timer_id =
466
if (id && *id == si->check_pointer_timer_id) /* this is us - it's expired */
467
si->check_pointer_timer_id = 0;
469
if (si->check_pointer_timer_id) /* only queue one at a time */
470
XtRemoveTimeOut (si->check_pointer_timer_id);
472
si->check_pointer_timer_id = /* now re-queue */
329
473
XtAppAddTimeOut (si->app, p->pointer_timeout, check_pointer_timer,
332
476
for (i = 0; i < si->nscreens; i++)
334
478
saver_screen_info *ssi = &si->screens[i];
336
int root_x, root_y, x, y;
339
XQueryPointer (si->dpy, ssi->screensaver_window, &root, &child,
340
&root_x, &root_y, &x, &y, &mask);
342
if (root_x == ssi->poll_mouse_last_root_x &&
343
root_y == ssi->poll_mouse_last_root_y &&
344
child == ssi->poll_mouse_last_child &&
345
mask == ssi->poll_mouse_last_mask)
352
if (root_x == ssi->poll_mouse_last_root_x &&
353
root_y == ssi->poll_mouse_last_root_y &&
354
child == ssi->poll_mouse_last_child)
355
fprintf (stderr, "%s: modifiers changed at %s on screen %d.\n",
356
blurb(), timestring(), i);
358
fprintf (stderr, "%s: pointer moved at %s on screen %d.\n",
359
blurb(), timestring(), i);
362
fprintf (stderr, "%s: old: %d %d 0x%x ; new: %d %d 0x%x\n",
364
ssi->poll_mouse_last_root_x,
365
ssi->poll_mouse_last_root_y,
366
(unsigned int) ssi->poll_mouse_last_child,
367
root_x, root_y, (unsigned int) child);
370
#endif /* DEBUG_TIMERS */
372
si->last_activity_screen = ssi;
373
ssi->poll_mouse_last_root_x = root_x;
374
ssi->poll_mouse_last_root_y = root_y;
375
ssi->poll_mouse_last_child = child;
376
ssi->poll_mouse_last_mask = mask;
479
if (pointer_moved_p (ssi, True))
379
483
#ifdef HAVE_PROC_INTERRUPTS
422
520
time_t now = time ((time_t *) 0);
423
521
long shift = now - si->last_wall_clock_time;
427
fprintf (stderr, "%s: checking wall clock (%d).\n", blurb(),
428
(si->last_wall_clock_time == 0 ? 0 : shift));
429
#endif /* DEBUG_TIMERS */
525
int i = (si->last_wall_clock_time == 0 ? 0 : shift);
527
"%s: checking wall clock for hibernation (%d:%02d:%02d).\n",
529
(i / (60 * 60)), ((i / 60) % 60), (i % 60));
431
532
if (si->last_wall_clock_time != 0 &&
432
533
shift > (p->timeout / 1000))
434
535
if (p->verbose_p)
435
fprintf (stderr, "%s: wall clock has jumped by %d:%02d:%02d!\n",
536
fprintf (stderr, "%s: wall clock has jumped by %ld:%02ld:%02ld!\n",
437
538
(shift / (60 * 60)), ((shift / 60) % 60), (shift % 60));
574
675
if (polling_for_idleness)
575
676
/* This causes a no-op event to be delivered to us in a while, so that
576
we come back around through the event loop again. Use of this timer
577
is economical: for example, if the screensaver should come on in 5
578
minutes, and the user has been idle for 2 minutes, then this
579
timeout will go off no sooner than 3 minutes from now. */
580
schedule_wakeup_event (si, p->timeout, p->verbose_p);
677
we come back around through the event loop again. */
678
schedule_wakeup_event (si, p->timeout, p->debug_p);
582
680
if (polling_mouse_position)
583
681
/* Check to see if the mouse has moved, and set up a repeating timer
689
795
case ButtonRelease:
690
796
case MotionNotify:
800
Window root=0, window=0;
802
const char *type = 0;
695
803
if (event.xany.type == MotionNotify)
696
fprintf (stderr,"%s: MotionNotify at %s\n",blurb(),timestring());
805
/*type = "MotionNotify";*/
806
root = event.xmotion.root;
807
window = event.xmotion.window;
808
x = event.xmotion.x_root;
809
y = event.xmotion.y_root;
697
811
else if (event.xany.type == KeyPress)
698
fprintf (stderr, "%s: KeyPress seen on 0x%X at %s\n", blurb(),
699
(unsigned int) event.xkey.window, timestring ());
814
root = event.xkey.root;
815
window = event.xkey.window;
700
818
else if (event.xany.type == ButtonPress)
701
fprintf (stderr, "%s: ButtonPress seen on 0x%X at %s\n", blurb(),
702
(unsigned int) event.xbutton.window, timestring ());
820
type = "ButtonPress";
821
root = event.xkey.root;
822
window = event.xkey.window;
823
x = event.xmotion.x_root;
824
y = event.xmotion.y_root;
830
for (i = 0; i < si->nscreens; i++)
831
if (root == RootWindowOfScreen (si->screens[i].screen))
833
fprintf (stderr,"%s: %d: %s on 0x%lx",
834
blurb(), i, type, (unsigned long) window);
836
/* Be careful never to do this unless in -debug mode, as
837
this could expose characters from the unlock password. */
838
if (p->debug_p && event.xany.type == KeyPress)
842
XLookupString (&event.xkey, &c, 1, &keysym, 0);
843
fprintf (stderr, " (%s%s)",
844
(event.xkey.send_event ? "synthetic " : ""),
845
XKeysymToString (keysym));
849
fprintf (stderr, "\n");
851
fprintf (stderr, " at %d,%d.\n", x, y);
704
#endif /* DEBUG_TIMERS */
706
855
/* If any widgets want to handle this event, let them. */
707
856
dispatch_event (si, &event);
859
/* If we got a MotionNotify event, figure out what screen it
860
was on and poll the mouse there: if the mouse hasn't moved
861
far enough to count as "real" motion, then ignore this
864
if (event.xany.type == MotionNotify)
867
for (i = 0; i < si->nscreens; i++)
868
if (event.xmotion.root ==
869
RootWindowOfScreen (si->screens[i].screen))
871
if (i < si->nscreens)
873
if (!pointer_moved_p (&si->screens[i], False))
709
879
/* We got a user event.
710
880
If we're waiting for the user to become active, this is it.
711
881
If we're waiting until the user becomes idle, reset the timers
814
984
#endif /* HAVE_SGI_SAVER_EXTENSION */
987
if (event.type == (si->randr_event_number + RRScreenChangeNotify))
989
/* The Resize and Rotate extension sends an event when the
990
size, rotation, or refresh rate of the screen has changed. */
992
XRRScreenChangeNotifyEvent *xrr_event =
993
(XRRScreenChangeNotifyEvent *) &event;
994
/* XRRRootToScreen is in Xrandr.h 1.4, 2001/06/07 */
995
int screen = XRRRootToScreen (si->dpy, xrr_event->window);
999
if (si->screens[screen].width == xrr_event->width &&
1000
si->screens[screen].height == xrr_event->height)
1002
"%s: %d: no-op screen size change event (%dx%d)\n",
1004
xrr_event->width, xrr_event->height);
1007
"%s: %d: screen size changed from %dx%d to %dx%d\n",
1009
si->screens[screen].width,
1010
si->screens[screen].height,
1011
xrr_event->width, xrr_event->height);
1014
# ifdef RRScreenChangeNotifyMask
1015
/* Inform Xlib that it's ok to update its data structures. */
1016
XRRUpdateConfiguration (&event); /* Xrandr.h 1.9, 2002/09/29 */
1017
# endif /* RRScreenChangeNotifyMask */
1019
/* Resize the existing xscreensaver windows and cached ssi data. */
1020
resize_screensaver_window (si);
1023
#endif /* HAVE_RANDR */
816
1025
/* Just some random event. Let the Widgets handle it, if desired. */
817
1026
dispatch_event (si, &event);
908
1117
0: 309453991 timer
909
1118
1: 4771729 keyboard
911
but on later kernels with MP machines, it looks like this:
1120
but in Linux 2.2 and 2.4 kernels with MP machines, it looks like this:
914
1123
0: 1671450 1672618 IO-APIC-edge timer
915
1124
1: 13037 13495 IO-APIC-edge keyboard
1126
and in Linux 2.6, it's gotten even goofier: now there are two lines
1127
labelled "i8042". One of them is the keyboard, and one of them is
1128
the PS/2 mouse -- and of course, you can't tell them apart, except
1129
by wiggling the mouse and noting which one changes:
1132
1: 32051 30864 IO-APIC-edge i8042
1133
12: 476577 479913 IO-APIC-edge i8042
917
1135
Joy! So how are we expected to parse that? Well, this code doesn't
918
parse it: it saves the last line with the string "keyboard" in it, and
919
does a string-comparison to note when it has changed.
921
Thanks to Nat Friedman <nat@nat.org> for figuring out all of this crap.
923
Note that this only checks for lines with "keyboard" or "PS/2 Mouse" in
924
them. If you have a serial mouse, it won't detect that, it will only detect
925
keyboard activity. That's because there's no way to tell the difference
926
between a serial mouse and a general serial port, and it would be somewhat
927
unfortunate to have the screensaver turn off when the modem on COM1 burped.
1136
parse it: it saves the first line with the string "keyboard" (or
1137
"i8042") in it, and does a string-comparison to note when it has
1138
changed. If there are two "i8042" lines, we assume the first is
1139
the keyboard and the second is the mouse (doesn't matter which is
1140
which, really, as long as we don't compare them against each other.)
1142
Thanks to Nat Friedman <nat@nat.org> for figuring out most of this crap.
1144
Note that if you have a serial or USB mouse, or a USB keyboard, it won't
1145
detect it. That's because there's no way to tell the difference between a
1146
serial mouse and a general serial port, and all USB devices look the same
1147
from here. It would be somewhat unfortunate to have the screensaver turn
1148
off when the modem on COM1 burped, or when a USB disk was accessed.
1016
/* Now read through the pseudo-file until we find the "keyboard" line. */
1238
/* Now read through the pseudo-file until we find the "keyboard",
1239
"PS/2 mouse", or "i8042" lines. */
1018
1241
while (fgets (new_line, sizeof(new_line)-1, f1))
1020
if (!got_kbd && strstr (new_line, "keyboard"))
1022
kbd_diff = (*last_kbd_line && !!strcmp (new_line, last_kbd_line));
1243
Bool i8042_p = !!strstr (new_line, "i8042");
1244
if (i8042_p) i8042_count++;
1246
if (strchr (new_line, ','))
1248
/* Ignore any line that has a comma on it: this is because
1251
12: 930935 XT-PIC usb-uhci, PS/2 Mouse
1253
is really bad news. It *looks* like we can note mouse
1254
activity from that line, but really, that interrupt gets
1255
fired any time any USB device has activity! So we have
1256
to ignore any shared IRQs.
1259
else if (!checked_kbd &&
1260
(strstr (new_line, "keyboard") ||
1261
(i8042_p && i8042_count == 1)))
1263
/* Assume the keyboard interrupt is the line that says "keyboard",
1264
or the *first* line that says "i8042".
1266
kbd_changed = (*last_kbd_line && !!strcmp (new_line, last_kbd_line));
1023
1267
strcpy (last_kbd_line, new_line);
1026
else if (!got_ptr && strstr (new_line, "PS/2 Mouse"))
1270
else if (!checked_ptr &&
1271
(strstr (new_line, "PS/2 Mouse") ||
1272
(i8042_p && i8042_count == 2)))
1028
ptr_diff = (*last_ptr_line && !!strcmp (new_line, last_ptr_line));
1274
/* Assume the mouse interrupt is the line that says "PS/2 mouse",
1275
or the *second* line that says "i8042".
1277
ptr_changed = (*last_ptr_line && !!strcmp (new_line, last_ptr_line));
1029
1278
strcpy (last_ptr_line, new_line);
1033
if (got_kbd && got_ptr)
1282
if (checked_kbd && checked_ptr)
1037
if (got_kbd || got_ptr)
1286
if (checked_kbd || checked_ptr)
1040
return (kbd_diff || ptr_diff);
1290
if (si->prefs.debug_p && (kbd_changed || ptr_changed))
1291
fprintf (stderr, "%s: /proc/interrupts activity: %s\n",
1293
((kbd_changed && ptr_changed) ? "mouse and kbd" :
1294
kbd_changed ? "kbd" :
1295
ptr_changed ? "mouse" : "ERR"));
1297
return (kbd_changed || ptr_changed);
1152
1407
si->watchdog_id = XtAppAddTimeOut (si->app, p->watchdog_timeout,
1153
1408
watchdog_timer, (XtPointer) si);
1157
1411
fprintf (stderr, "%s: restarting watchdog_timer (%ld, %ld)\n",
1158
1412
blurb(), p->watchdog_timeout, si->watchdog_id);
1159
#endif /* DEBUG_TIMERS */
1417
/* It's possible that a race condition could have led to the saver
1418
window being unexpectedly still mapped. This can happen like so:
1422
- that hack tries to grab a screen image (it does this by
1423
first unmapping the saver window, then remapping it.)
1424
- hack unmaps window
1426
- user becomes active
1427
- hack re-maps window (*)
1428
- driver kills subprocess
1429
- driver unmaps window (**)
1431
The race is that (*) might have been sent to the server before
1432
the client process was killed, but, due to scheduling randomness,
1433
might not have been received by the server until after (**).
1434
In other words, (*) and (**) might happen out of order, meaning
1435
the driver will unmap the window, and then after that, the
1436
recently-dead client will re-map it. This leaves the user
1437
locked out (it looks like a desktop, but it's not!)
1439
To avoid this: after un-blanking the screen, we launch a timer
1440
that wakes up once a second for ten seconds, and makes damned
1441
sure that the window is still unmapped.
1445
de_race_timer (XtPointer closure, XtIntervalId *id)
1447
saver_info *si = (saver_info *) closure;
1448
saver_preferences *p = &si->prefs;
1451
if (id == 0) /* if id is 0, this is the initialization call. */
1453
si->de_race_ticks = 10;
1455
fprintf (stderr, "%s: starting de-race timer (%d seconds.)\n",
1456
blurb(), si->de_race_ticks);
1461
XSync (si->dpy, False);
1462
for (i = 0; i < si->nscreens; i++)
1464
saver_screen_info *ssi = &si->screens[i];
1465
Window w = ssi->screensaver_window;
1466
XWindowAttributes xgwa;
1467
XGetWindowAttributes (si->dpy, w, &xgwa);
1468
if (xgwa.map_state != IsUnmapped)
1472
"%s: %d: client race! emergency unmap 0x%lx.\n",
1473
blurb(), i, (unsigned long) w);
1474
XUnmapWindow (si->dpy, w);
1476
else if (p->debug_p)
1477
fprintf (stderr, "%s: %d: (de-race of 0x%lx is cool.)\n",
1478
blurb(), i, (unsigned long) w);
1480
XSync (si->dpy, False);
1482
si->de_race_ticks--;
1485
if (id && *id == si->de_race_id)
1488
if (si->de_race_id) abort();
1490
if (si->de_race_ticks <= 0)
1494
fprintf (stderr, "%s: de-race completed.\n", blurb());
1498
si->de_race_id = XtAppAddTimeOut (si->app, secs * 1000,
1499
de_race_timer, closure);