1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /* Metacity 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, see <http://www.gnu.org/licenses/>.
21 */
22
23 /**
24 * \file bell.c Ring the bell or flash the screen
25 *
26 * Sometimes, X programs "ring the bell", whatever that means. Metacity lets
27 * the user configure the bell to be audible or visible (aka visual), and
28 * if it's visual it can be configured to be frame-flash or fullscreen-flash.
29 * We never get told about audible bells; X handles them just fine by itself.
30 *
31 * Visual bells come in at meta_bell_notify(), which checks we are actually
32 * in visual mode and calls through to bell_visual_notify(). That
33 * function then checks what kind of visual flash you like, and calls either
34 * bell_flash_fullscreen()-- which calls bell_flash_screen() to do
35 * its work-- or bell_flash_frame(), which flashes the focussed window
36 * using bell_flash_window_frame(), unless there is no such window, in
37 * which case it flashes the screen instead. bell_flash_window_frame()
38 * flashes the frame and calls bell_unflash_frame() as a timeout to
39 * remove the flash.
40 *
41 * The visual bell was the result of a discussion in Bugzilla here:
42 * <http://bugzilla.gnome.org/show_bug.cgi?id=99886>.
43 *
44 * Several of the functions in this file are ifdeffed out entirely if we are
45 * found not to have the XKB extension, which is required to do these clever
46 * things with bells; some others are entirely no-ops in that case.
47 */
48
49 #include <config.h>
50 #include "bell.h"
51 #include "screen-private.h"
52 #include "prefs.h"
53
54 #ifdef HAVE_CANBERRA
55 #include <canberra-gtk.h>
56 #endif
57
58 /**
59 * Flashes one entire screen. This is done by making a window the size of the
60 * whole screen (or reusing the old one, if it's still around), mapping it,
61 * painting it white and then black, and then unmapping it. We set saveunder so
62 * that all the windows behind it come back immediately.
63 *
64 * Unlike frame flashes, we don't do fullscreen flashes with a timeout; rather,
65 * we do them in one go, because we don't have to rely on the theme code
66 * redrawing the frame for us in order to do the flash.
67 *
68 * \param display The display which owns the screen (rather redundant)
69 * \param screen The screen to flash
70 *
71 * \bug The way I read it, this appears not to do the flash
72 * the first time we flash a particular display. Am I wrong?
73 *
74 * \bug This appears to destroy our current XSync status.
75 */
76 static void
bell_flash_screen(MetaDisplay * display,MetaScreen * screen)77 bell_flash_screen (MetaDisplay *display,
78 MetaScreen *screen)
79 {
80 Window root = screen->xroot;
81 int width = screen->rect.width;
82 int height = screen->rect.height;
83
84 if (screen->flash_window == None)
85 {
86 Visual *visual = (Visual *)CopyFromParent;
87 XSetWindowAttributes xswa;
88 int depth = CopyFromParent;
89 xswa.save_under = True;
90 xswa.override_redirect = True;
91 /*
92 * TODO: use XGetVisualInfo and determine which is an
93 * overlay, if one is present, and use the Overlay visual
94 * for this window (for performance reasons).
95 * Not sure how to tell this yet...
96 */
97 screen->flash_window = XCreateWindow (display->xdisplay, root,
98 0, 0, width, height,
99 0, depth,
100 InputOutput,
101 visual,
102 /* note: XSun doesn't like SaveUnder here */
103 CWSaveUnder | CWOverrideRedirect,
104 &xswa);
105 XSelectInput (display->xdisplay, screen->flash_window, ExposureMask);
106 XMapWindow (display->xdisplay, screen->flash_window);
107 XSync (display->xdisplay, False);
108 XFlush (display->xdisplay);
109 XUnmapWindow (display->xdisplay, screen->flash_window);
110 }
111 else
112 {
113 /* just draw something in the window */
114 GC gc = XCreateGC (display->xdisplay, screen->flash_window, 0, NULL);
115 XMapWindow (display->xdisplay, screen->flash_window);
116 XSetForeground (display->xdisplay, gc,
117 WhitePixel (display->xdisplay,
118 XScreenNumberOfScreen (screen->xscreen)));
119 XFillRectangle (display->xdisplay, screen->flash_window, gc,
120 0, 0, width, height);
121 XSetForeground (display->xdisplay, gc,
122 BlackPixel (display->xdisplay,
123 XScreenNumberOfScreen (screen->xscreen)));
124 XFillRectangle (display->xdisplay, screen->flash_window, gc,
125 0, 0, width, height);
126 XFlush (display->xdisplay);
127 XSync (display->xdisplay, False);
128 XUnmapWindow (display->xdisplay, screen->flash_window);
129 XFreeGC (display->xdisplay, gc);
130 }
131
132 if (meta_prefs_get_focus_mode () != G_DESKTOP_FOCUS_MODE_CLICK &&
133 !display->mouse_mode)
134 meta_display_increment_focus_sentinel (display);
135 XFlush (display->xdisplay);
136 }
137
138 /**
139 * Flashes one screen, or all screens, in response to a bell event.
140 * If the event is on a particular window, flash the screen that
141 * window is on. Otherwise, flash every screen on this display.
142 *
143 * If the configure script found we had no XKB, this does not exist.
144 *
145 * \param display The display the event came in on
146 * \param xkb_ev The bell event
147 */
148 #ifdef HAVE_XKB
149 static void
bell_flash_fullscreen(MetaDisplay * display,XkbAnyEvent * xkb_ev)150 bell_flash_fullscreen (MetaDisplay *display,
151 XkbAnyEvent *xkb_ev)
152 {
153 g_assert (xkb_ev->xkb_type == XkbBellNotify);
154
155 bell_flash_screen (display, display->screen);
156 }
157
158 /**
159 * Makes a frame be not flashed; this is the timeout half of
160 * bell_flash_window_frame(). This is done simply by clearing the
161 * flash flag and queuing a redraw of the frame.
162 *
163 * If the configure script found we had no XKB, this does not exist.
164 *
165 * \param data The frame to unflash, cast to a gpointer so it can go into
166 * a callback function.
167 * \return Always FALSE, so we don't get called again.
168 *
169 * \bug This is the parallel to bell_flash_window_frame(), so it should
170 * really be called meta_bell_unflash_window_frame().
171 */
172 static gboolean
bell_unflash_frame(gpointer data)173 bell_unflash_frame (gpointer data)
174 {
175 MetaFrame *frame = (MetaFrame *) data;
176 frame->is_flashing = 0;
177 meta_frame_queue_draw (frame);
178 return FALSE;
179 }
180
181 /**
182 * Makes a frame flash and then return to normal shortly afterwards.
183 * This is done by setting a flag so that the theme
184 * code will temporarily draw the frame as focussed if it's unfocussed and
185 * vice versa, and then queueing a redraw. Lastly, we create a timeout so
186 * that the flag can be unset and the frame re-redrawn.
187 *
188 * If the configure script found we had no XKB, this does not exist.
189 *
190 * \param window The window to flash
191 */
192 static void
bell_flash_window_frame(MetaWindow * window)193 bell_flash_window_frame (MetaWindow *window)
194 {
195 g_assert (window->frame != NULL);
196 window->frame->is_flashing = 1;
197 meta_frame_queue_draw (window->frame);
198 g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, 100,
199 bell_unflash_frame, window->frame, NULL);
200 }
201
202 /**
203 * Flashes the frame of the focussed window. If there is no focussed window,
204 * flashes the screen.
205 *
206 * \param display The display the bell event came in on
207 * \param xkb_ev The bell event we just received
208 */
209 static void
bell_flash_frame(MetaDisplay * display,XkbAnyEvent * xkb_ev)210 bell_flash_frame (MetaDisplay *display,
211 XkbAnyEvent *xkb_ev)
212 {
213 XkbBellNotifyEvent *xkb_bell_event = (XkbBellNotifyEvent *) xkb_ev;
214 MetaWindow *window;
215
216 g_assert (xkb_ev->xkb_type == XkbBellNotify);
217 window = meta_display_lookup_x_window (display, xkb_bell_event->window);
218 if (!window && (display->focus_window))
219 {
220 window = display->focus_window;
221 }
222 if (window && window->frame)
223 {
224 bell_flash_window_frame (window);
225 }
226 else /* revert to fullscreen flash if there's no focussed window */
227 {
228 bell_flash_fullscreen (display, xkb_ev);
229 }
230 }
231
232 /**
233 * Gives the user some kind of visual bell substitute, in response to a
234 * bell event. What this is depends on the "visual bell type" pref.
235 *
236 * If the configure script found we had no XKB, this does not exist.
237 *
238 * \param display The display the bell event came in on
239 * \param xkb_ev The bell event we just received
240 *
241 * \bug This should be merged with meta_bell_notify().
242 */
243 static void
bell_visual_notify(MetaDisplay * display,XkbAnyEvent * xkb_ev)244 bell_visual_notify (MetaDisplay *display,
245 XkbAnyEvent *xkb_ev)
246 {
247 switch (meta_prefs_get_visual_bell_type ())
248 {
249 case G_DESKTOP_VISUAL_BELL_FULLSCREEN_FLASH:
250 bell_flash_fullscreen (display, xkb_ev);
251 break;
252 case G_DESKTOP_VISUAL_BELL_FRAME_FLASH:
253 bell_flash_frame (display, xkb_ev); /* does nothing yet */
254 break;
255 default:
256 break;
257 }
258 }
259
260 void
meta_bell_notify(MetaDisplay * display,XkbAnyEvent * xkb_ev)261 meta_bell_notify (MetaDisplay *display,
262 XkbAnyEvent *xkb_ev)
263 {
264 /* flash something */
265 if (meta_prefs_get_visual_bell ())
266 bell_visual_notify (display, xkb_ev);
267
268 if (meta_prefs_bell_is_audible ())
269 {
270 XkbBellNotifyEvent *xkb_bell_event = (XkbBellNotifyEvent*) xkb_ev;
271
272 #ifdef HAVE_CANBERRA
273 ca_proplist *p;
274 MetaWindow *window;
275 int res;
276
277 ca_proplist_create (&p);
278 ca_proplist_sets (p, CA_PROP_EVENT_ID, "bell-window-system");
279 ca_proplist_sets (p, CA_PROP_EVENT_DESCRIPTION, _("Bell event"));
280 ca_proplist_sets (p, CA_PROP_CANBERRA_CACHE_CONTROL, "permanent");
281
282 window = meta_display_lookup_x_window (display, xkb_bell_event->window);
283 if (!window && (display->focus_window) && (display->focus_window->frame))
284 window = display->focus_window;
285
286 if (window)
287 {
288 pid_t client_pid;
289
290 client_pid = meta_window_get_client_pid (window);
291
292 ca_proplist_sets (p, CA_PROP_WINDOW_NAME, window->title);
293 ca_proplist_setf (p, CA_PROP_WINDOW_X11_XID, "%lu", (unsigned long)window->xwindow);
294 ca_proplist_sets (p, CA_PROP_APPLICATION_NAME, window->res_name);
295 ca_proplist_setf (p, CA_PROP_APPLICATION_PROCESS_ID, "%d", client_pid);
296 }
297
298 /* First, we try to play a real sound ... */
299 res = ca_context_play_full (ca_gtk_context_get (), 1, p, NULL, NULL);
300
301 ca_proplist_destroy (p);
302
303 if (res != CA_SUCCESS && res != CA_ERROR_DISABLED)
304 #else
305 if (1)
306 #endif /* HAVE_CANBERRA */
307 {
308 /* ...and in case that failed we use the classic X11 bell. */
309 XkbForceDeviceBell (display->xdisplay,
310 xkb_bell_event->device,
311 xkb_bell_event->bell_class,
312 xkb_bell_event->bell_id,
313 xkb_bell_event->percent);
314 }
315 }
316 }
317 #endif /* HAVE_XKB */
318
319 void
meta_bell_set_audible(MetaDisplay * display,gboolean audible)320 meta_bell_set_audible (MetaDisplay *display, gboolean audible)
321 {
322 }
323
324 gboolean
meta_bell_init(MetaDisplay * display)325 meta_bell_init (MetaDisplay *display)
326 {
327 #ifdef HAVE_XKB
328 int xkb_base_error_type, xkb_opcode;
329
330 if (!XkbQueryExtension (display->xdisplay,
331 &xkb_opcode,
332 &display->xkb_base_event_type,
333 &xkb_base_error_type,
334 NULL,
335 NULL))
336 {
337 display->xkb_base_event_type = -1;
338 g_message ("could not find XKB extension.");
339 return FALSE;
340 }
341 else
342 {
343 XkbSelectEvents (display->xdisplay,
344 XkbUseCoreKbd,
345 XkbBellNotifyMask,
346 XkbBellNotifyMask);
347 XkbChangeEnabledControls (display->xdisplay,
348 XkbUseCoreKbd,
349 XkbAudibleBellMask,
350 0);
351
352 return TRUE;
353 }
354 #endif
355 return FALSE;
356 }
357
358 /**
359 * Deals with a frame being destroyed. This is important because if we're
360 * using a visual bell, we might be flashing the edges of the frame, and
361 * so we'd have a timeout function waiting ready to un-flash them. If the
362 * frame's going away, we can tell the timeout not to bother.
363 *
364 * \param frame The frame which is being destroyed
365 */
366 void
meta_bell_notify_frame_destroy(MetaFrame * frame)367 meta_bell_notify_frame_destroy (MetaFrame *frame)
368 {
369 if (frame->is_flashing)
370 g_source_remove_by_funcs_user_data (&g_timeout_funcs, frame);
371 }
372