File: | core/bell.c |
Warning: | line 397, column 2 This statement is never executed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ |
2 | |
3 | /* Croma visual bell */ |
4 | |
5 | /* |
6 | * Copyright (C) 2002 Sun Microsystems Inc. |
7 | * Copyright (C) 2005, 2006 Elijah Newren |
8 | * |
9 | * This program is free software; you can redistribute it and/or |
10 | * modify it under the terms of the GNU General Public License as |
11 | * published by the Free Software Foundation; either version 2 of the |
12 | * License, or (at your option) any later version. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, but |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | * General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
22 | * 02110-1301, USA. |
23 | */ |
24 | |
25 | /** |
26 | * \file bell.c Ring the bell or flash the screen |
27 | * |
28 | * Sometimes, X programs "ring the bell", whatever that means. Croma lets |
29 | * the user configure the bell to be audible or visible (aka visual), and |
30 | * if it's visual it can be configured to be frame-flash or fullscreen-flash. |
31 | * We never get told about audible bells; X handles them just fine by itself. |
32 | * |
33 | * Visual bells come in at meta_bell_notify(), which checks we are actually |
34 | * in visual mode and calls through to bell_visual_notify(). That |
35 | * function then checks what kind of visual flash you like, and calls either |
36 | * bell_flash_fullscreen()-- which calls bell_flash_screen() to do |
37 | * its work-- or bell_flash_frame(), which flashes the focussed window |
38 | * using bell_flash_window_frame(), unless there is no such window, in |
39 | * which case it flashes the screen instead. bell_flash_window_frame() |
40 | * flashes the frame and calls bell_unflash_frame() as a timeout to |
41 | * remove the flash. |
42 | * |
43 | * The visual bell was the result of a discussion in Bugzilla here: |
44 | * <http://bugzilla.gnome.org/show_bug.cgi?id=99886>. |
45 | * |
46 | * Several of the functions in this file are ifdeffed out entirely if we are |
47 | * found not to have the XKB extension, which is required to do these clever |
48 | * things with bells; some others are entirely no-ops in that case. |
49 | */ |
50 | |
51 | #include <config.h> |
52 | #include "bell.h" |
53 | #include "screen-private.h" |
54 | #include "prefs.h" |
55 | #include <kanberra-ctk.h> |
56 | |
57 | /** |
58 | * Flashes one entire screen. This is done by making a window the size of the |
59 | * whole screen (or reusing the old one, if it's still around), mapping it, |
60 | * painting it white and then black, and then unmapping it. We set saveunder so |
61 | * that all the windows behind it come back immediately. |
62 | * |
63 | * Unlike frame flashes, we don't do fullscreen flashes with a timeout; rather, |
64 | * we do them in one go, because we don't have to rely on the theme code |
65 | * redrawing the frame for us in order to do the flash. |
66 | * |
67 | * \param display The display which owns the screen (rather redundant) |
68 | * \param screen The screen to flash |
69 | * |
70 | * \bug The way I read it, this appears not to do the flash |
71 | * the first time we flash a particular display. Am I wrong? |
72 | * |
73 | * \bug This appears to destroy our current XSync status. |
74 | */ |
75 | static void |
76 | bell_flash_screen (MetaDisplay *display, |
77 | MetaScreen *screen) |
78 | { |
79 | Window root = screen->xroot; |
80 | int width = screen->rect.width; |
81 | int height = screen->rect.height; |
82 | |
83 | if (screen->flash_window == None0L) |
84 | { |
85 | Visual *visual = (Visual *)CopyFromParent0L; |
86 | XSetWindowAttributes xswa; |
87 | int depth = CopyFromParent0L; |
88 | xswa.save_under = True1; |
89 | xswa.override_redirect = True1; |
90 | /* |
91 | * TODO: use XGetVisualInfo and determine which is an |
92 | * overlay, if one is present, and use the Overlay visual |
93 | * for this window (for performance reasons). |
94 | * Not sure how to tell this yet... |
95 | */ |
96 | screen->flash_window = XCreateWindow (display->xdisplay, root, |
97 | 0, 0, width, height, |
98 | 0, depth, |
99 | InputOutput1, |
100 | visual, |
101 | /* note: XSun doesn't like SaveUnder here */ |
102 | CWSaveUnder(1L<<10) | CWOverrideRedirect(1L<<9), |
103 | &xswa); |
104 | XSelectInput (display->xdisplay, screen->flash_window, ExposureMask(1L<<15)); |
105 | XMapWindow (display->xdisplay, screen->flash_window); |
106 | XSync (display->xdisplay, False0); |
107 | XFlush (display->xdisplay); |
108 | XUnmapWindow (display->xdisplay, screen->flash_window); |
109 | } |
110 | else |
111 | { |
112 | /* just draw something in the window */ |
113 | GC gc = XCreateGC (display->xdisplay, screen->flash_window, 0, NULL((void*)0)); |
114 | XMapWindow (display->xdisplay, screen->flash_window); |
115 | XSetForeground (display->xdisplay, gc, |
116 | WhitePixel (display->xdisplay,((&((_XPrivDisplay)(display->xdisplay))->screens[XScreenNumberOfScreen (screen->xscreen)])->white_pixel) |
117 | XScreenNumberOfScreen (screen->xscreen))((&((_XPrivDisplay)(display->xdisplay))->screens[XScreenNumberOfScreen (screen->xscreen)])->white_pixel)); |
118 | XFillRectangle (display->xdisplay, screen->flash_window, gc, |
119 | 0, 0, width, height); |
120 | XSetForeground (display->xdisplay, gc, |
121 | BlackPixel (display->xdisplay,((&((_XPrivDisplay)(display->xdisplay))->screens[XScreenNumberOfScreen (screen->xscreen)])->black_pixel) |
122 | XScreenNumberOfScreen (screen->xscreen))((&((_XPrivDisplay)(display->xdisplay))->screens[XScreenNumberOfScreen (screen->xscreen)])->black_pixel)); |
123 | XFillRectangle (display->xdisplay, screen->flash_window, gc, |
124 | 0, 0, width, height); |
125 | XFlush (display->xdisplay); |
126 | XSync (display->xdisplay, False0); |
127 | XUnmapWindow (display->xdisplay, screen->flash_window); |
128 | XFreeGC (display->xdisplay, gc); |
129 | } |
130 | |
131 | if (meta_prefs_get_focus_mode () != META_FOCUS_MODE_CLICK && |
132 | !display->mouse_mode) |
133 | meta_display_increment_focus_sentinel (display); |
134 | XFlush (display->xdisplay); |
135 | } |
136 | |
137 | /** |
138 | * Flashes one screen, or all screens, in response to a bell event. |
139 | * If the event is on a particular window, flash the screen that |
140 | * window is on. Otherwise, flash every screen on this display. |
141 | * |
142 | * If the configure script found we had no XKB, this does not exist. |
143 | * |
144 | * \param display The display the event came in on |
145 | * \param xkb_ev The bell event |
146 | */ |
147 | #ifdef HAVE_XKB |
148 | static void |
149 | bell_flash_fullscreen (MetaDisplay *display, |
150 | XkbAnyEvent *xkb_ev) |
151 | { |
152 | XkbBellNotifyEvent *xkb_bell_ev = (XkbBellNotifyEvent *) xkb_ev; |
153 | MetaScreen *screen; |
154 | |
155 | g_assert (xkb_ev->xkb_type == XkbBellNotify)do { if (xkb_ev->xkb_type == 8) ; else g_assertion_message_expr ("croma", "core/bell.c", 155, ((const char*) (__func__)), "xkb_ev->xkb_type == XkbBellNotify" ); } while (0); |
156 | if (xkb_bell_ev->window != None0L) |
157 | { |
158 | screen = meta_display_screen_for_xwindow (display, xkb_bell_ev->window); |
159 | if (screen) |
160 | bell_flash_screen (display, screen); |
161 | } |
162 | else |
163 | { |
164 | GSList *screen_list = display->screens; |
165 | while (screen_list) |
166 | { |
167 | screen = (MetaScreen *) screen_list->data; |
168 | bell_flash_screen (display, screen); |
169 | screen_list = screen_list->next; |
170 | } |
171 | } |
172 | } |
173 | |
174 | /** |
175 | * Makes a frame be not flashed; this is the timeout half of |
176 | * bell_flash_window_frame(). This is done simply by clearing the |
177 | * flash flag and queuing a redraw of the frame. |
178 | * |
179 | * If the configure script found we had no XKB, this does not exist. |
180 | * |
181 | * \param data The frame to unflash, cast to a gpointer so it can go into |
182 | * a callback function. |
183 | * \return Always FALSE, so we don't get called again. |
184 | * |
185 | * \bug This is the parallel to bell_flash_window_frame(), so it should |
186 | * really be called meta_bell_unflash_window_frame(). |
187 | */ |
188 | static gboolean |
189 | bell_unflash_frame (gpointer data) |
190 | { |
191 | MetaFrame *frame = (MetaFrame *) data; |
192 | frame->is_flashing = 0; |
193 | meta_frame_queue_draw (frame); |
194 | return FALSE(0); |
195 | } |
196 | |
197 | /** |
198 | * Makes a frame flash and then return to normal shortly afterwards. |
199 | * This is done by setting a flag so that the theme |
200 | * code will temporarily draw the frame as focussed if it's unfocussed and |
201 | * vice versa, and then queueing a redraw. Lastly, we create a timeout so |
202 | * that the flag can be unset and the frame re-redrawn. |
203 | * |
204 | * If the configure script found we had no XKB, this does not exist. |
205 | * |
206 | * \param window The window to flash |
207 | */ |
208 | static void |
209 | bell_flash_window_frame (MetaWindow *window) |
210 | { |
211 | g_assert (window->frame != NULL)do { if (window->frame != ((void*)0)) ; else g_assertion_message_expr ("croma", "core/bell.c", 211, ((const char*) (__func__)), "window->frame != NULL" ); } while (0); |
212 | window->frame->is_flashing = 1; |
213 | meta_frame_queue_draw (window->frame); |
214 | g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE200, 100, |
215 | bell_unflash_frame, window->frame, NULL((void*)0)); |
216 | } |
217 | |
218 | /** |
219 | * Flashes the frame of the focussed window. If there is no focussed window, |
220 | * flashes the screen. |
221 | * |
222 | * \param display The display the bell event came in on |
223 | * \param xkb_ev The bell event we just received |
224 | */ |
225 | static void |
226 | bell_flash_frame (MetaDisplay *display, |
227 | XkbAnyEvent *xkb_ev) |
228 | { |
229 | XkbBellNotifyEvent *xkb_bell_event = (XkbBellNotifyEvent *) xkb_ev; |
230 | MetaWindow *window; |
231 | |
232 | g_assert (xkb_ev->xkb_type == XkbBellNotify)do { if (xkb_ev->xkb_type == 8) ; else g_assertion_message_expr ("croma", "core/bell.c", 232, ((const char*) (__func__)), "xkb_ev->xkb_type == XkbBellNotify" ); } while (0); |
233 | window = meta_display_lookup_x_window (display, xkb_bell_event->window); |
234 | if (!window && (display->focus_window)) |
235 | { |
236 | window = display->focus_window; |
237 | } |
238 | if (window && window->frame) |
239 | { |
240 | bell_flash_window_frame (window); |
241 | } |
242 | else /* revert to fullscreen flash if there's no focussed window */ |
243 | { |
244 | bell_flash_fullscreen (display, xkb_ev); |
245 | } |
246 | } |
247 | |
248 | /** |
249 | * Gives the user some kind of visual bell substitute, in response to a |
250 | * bell event. What this is depends on the "visual bell type" pref. |
251 | * |
252 | * If the configure script found we had no XKB, this does not exist. |
253 | * |
254 | * \param display The display the bell event came in on |
255 | * \param xkb_ev The bell event we just received |
256 | * |
257 | * \bug This should be merged with meta_bell_notify(). |
258 | */ |
259 | static void |
260 | bell_visual_notify (MetaDisplay *display, |
261 | XkbAnyEvent *xkb_ev) |
262 | { |
263 | switch (meta_prefs_get_visual_bell_type ()) |
264 | { |
265 | case META_VISUAL_BELL_FULLSCREEN_FLASH: |
266 | bell_flash_fullscreen (display, xkb_ev); |
267 | break; |
268 | case META_VISUAL_BELL_FRAME_FLASH: |
269 | bell_flash_frame (display, xkb_ev); /* does nothing yet */ |
270 | break; |
271 | case META_VISUAL_BELL_INVALID: |
272 | /* do nothing */ |
273 | break; |
274 | } |
275 | } |
276 | |
277 | void |
278 | meta_bell_notify (MetaDisplay *display, |
279 | XkbAnyEvent *xkb_ev) |
280 | { |
281 | /* flash something */ |
282 | if (meta_prefs_get_visual_bell ()) |
283 | bell_visual_notify (display, xkb_ev); |
284 | |
285 | if (meta_prefs_bell_is_audible ()) |
286 | { |
287 | ka_proplist *p; |
288 | XkbBellNotifyEvent *xkb_bell_event = (XkbBellNotifyEvent*) xkb_ev; |
289 | MetaWindow *window; |
290 | int res; |
291 | |
292 | ka_proplist_create (&p); |
293 | ka_proplist_sets (p, KA_PROP_EVENT_ID"event.id", "bell-window-system"); |
294 | ka_proplist_sets (p, KA_PROP_EVENT_DESCRIPTION"event.description", _("Bell event")dgettext ("croma", "Bell event")); |
295 | ka_proplist_sets (p, KA_PROP_KANBERRA_CACHE_CONTROL"kanberra.cache-control", "permanent"); |
296 | |
297 | window = meta_display_lookup_x_window (display, xkb_bell_event->window); |
298 | if (!window && (display->focus_window) && (display->focus_window->frame)) |
299 | window = display->focus_window; |
300 | |
301 | if (window) |
302 | { |
303 | int x=-1, y=-1, width=-1, height=-1, screen_width=-1, screen_height=-1; |
304 | MetaScreen *screen; |
305 | |
306 | screen = meta_window_get_screen (window); |
307 | |
308 | ka_proplist_sets (p, KA_PROP_WINDOW_NAME"window.name", window->title); |
309 | ka_proplist_setf (p, KA_PROP_WINDOW_X11_XID"window.x11.xid", "%lu", (unsigned long)window->xwindow); |
310 | ka_proplist_setf (p, KA_PROP_WINDOW_X11_SCREEN"window.x11.screen", "%i", meta_screen_get_screen_number(screen)); |
311 | ka_proplist_sets (p, KA_PROP_APPLICATION_NAME"application.name", window->res_name); |
312 | ka_proplist_setf (p, KA_PROP_APPLICATION_PROCESS_ID"application.process.id", "%d", window->net_wm_pid); |
313 | |
314 | /* properties for positional sound based on window placement */ |
315 | meta_window_get_geometry (window, &x, &y, &width, &height); |
316 | ka_proplist_setf (p, KA_PROP_WINDOW_X"window.x", "%i", x); |
317 | ka_proplist_setf (p, KA_PROP_WINDOW_Y"window.y", "%i", y); |
318 | ka_proplist_setf (p, KA_PROP_WINDOW_WIDTH"window.width", "%i", width); |
319 | ka_proplist_setf (p, KA_PROP_WINDOW_HEIGHT"window.height", "%i", height); |
320 | |
321 | meta_screen_get_size (screen, &screen_width, &screen_height); |
322 | if (screen_width > 1) |
323 | { |
324 | x += width/2; |
325 | x = CLAMP(x, 0, screen_width-1)(((x) > (screen_width-1)) ? (screen_width-1) : (((x) < ( 0)) ? (0) : (x))); |
326 | |
327 | /* From libkanberra-ctk. |
328 | * We use these strange format strings here to avoid that libc |
329 | * applies locale information on the formatting of floating |
330 | * numbers. */ |
331 | |
332 | ka_proplist_setf (p, KA_PROP_WINDOW_HPOS"window.hpos", "%i.%03i", |
333 | (int) (x/(screen_width-1)), (int) (1000.0*x/(screen_width-1)) % 1000); |
334 | } |
335 | if (screen_height > 1) |
336 | { |
337 | y += height/2; |
338 | y = CLAMP(y, 0, screen_height-1)(((y) > (screen_height-1)) ? (screen_height-1) : (((y) < (0)) ? (0) : (y))); |
339 | |
340 | ka_proplist_setf (p, KA_PROP_WINDOW_VPOS"window.vpos", "%i.%03i", |
341 | (int) (y/(screen_height-1)), (int) (1000.0*y/(screen_height-1)) % 1000); |
342 | } |
343 | } |
344 | |
345 | /* First, we try to play a real sound ... */ |
346 | res = ka_context_play_full (ka_ctk_context_get (), 1, p, NULL((void*)0), NULL((void*)0)); |
347 | |
348 | ka_proplist_destroy (p); |
349 | |
350 | if (res != KA_SUCCESS && res != KA_ERROR_DISABLED) |
351 | { |
352 | /* ...and in case that failed we use the classic X11 bell. */ |
353 | XkbForceDeviceBell (display->xdisplay, |
354 | xkb_bell_event->device, |
355 | xkb_bell_event->bell_class, |
356 | xkb_bell_event->bell_id, |
357 | xkb_bell_event->percent); |
358 | } |
359 | } |
360 | } |
361 | #endif /* HAVE_XKB */ |
362 | |
363 | void |
364 | meta_bell_set_audible (MetaDisplay *display, gboolean audible) |
365 | { |
366 | } |
367 | |
368 | gboolean |
369 | meta_bell_init (MetaDisplay *display) |
370 | { |
371 | #ifdef HAVE_XKB |
372 | int xkb_base_error_type, xkb_opcode; |
373 | |
374 | if (!XkbQueryExtension (display->xdisplay, &xkb_opcode, |
375 | &display->xkb_base_event_type, |
376 | &xkb_base_error_type, |
377 | NULL((void*)0), NULL((void*)0))) |
378 | { |
379 | display->xkb_base_event_type = -1; |
380 | g_message ("could not find XKB extension."); |
381 | return FALSE(0); |
382 | } |
383 | else |
384 | { |
385 | unsigned int mask = XkbBellNotifyMask(1L << 8); |
386 | gboolean visual_bell_auto_reset = FALSE(0); |
387 | /* TRUE if and when non-broken version is available */ |
388 | XkbSelectEvents (display->xdisplay, |
389 | XkbUseCoreKbd0x0100, |
390 | XkbBellNotifyMask(1L << 8), |
391 | XkbBellNotifyMask(1L << 8)); |
392 | XkbChangeEnabledControls (display->xdisplay, |
393 | XkbUseCoreKbd0x0100, |
394 | XkbAudibleBellMask(1L << 9), |
395 | 0); |
396 | if (visual_bell_auto_reset) { |
397 | XkbSetAutoResetControls (display->xdisplay, |
This statement is never executed | |
398 | XkbAudibleBellMask(1L << 9), |
399 | &mask, |
400 | &mask); |
401 | } |
402 | return TRUE(!(0)); |
403 | } |
404 | #endif |
405 | return FALSE(0); |
406 | } |
407 | |
408 | void |
409 | meta_bell_shutdown (MetaDisplay *display) |
410 | { |
411 | #ifdef HAVE_XKB |
412 | /* TODO: persist initial bell state in display, reset here */ |
413 | XkbChangeEnabledControls (display->xdisplay, |
414 | XkbUseCoreKbd0x0100, |
415 | XkbAudibleBellMask(1L << 9), |
416 | XkbAudibleBellMask(1L << 9)); |
417 | #endif |
418 | } |
419 | |
420 | /** |
421 | * Deals with a frame being destroyed. This is important because if we're |
422 | * using a visual bell, we might be flashing the edges of the frame, and |
423 | * so we'd have a timeout function waiting ready to un-flash them. If the |
424 | * frame's going away, we can tell the timeout not to bother. |
425 | * |
426 | * \param frame The frame which is being destroyed |
427 | */ |
428 | void |
429 | meta_bell_notify_frame_destroy (MetaFrame *frame) |
430 | { |
431 | if (frame->is_flashing) |
432 | g_source_remove_by_funcs_user_data (&g_timeout_funcs, frame); |
433 | } |