| File: | core/bell.c |
| Warning: | line 405, column 10 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, |
| 398 | XkbAudibleBellMask(1L << 9), |
| 399 | &mask, |
| 400 | &mask); |
| 401 | } |
| 402 | return TRUE(!(0)); |
| 403 | } |
| 404 | #endif |
| 405 | return FALSE(0); |
This statement is never executed | |
| 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 | } |