diff options
Diffstat (limited to 'src/core/bell.c')
-rw-r--r-- | src/core/bell.c | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/src/core/bell.c b/src/core/bell.c new file mode 100644 index 00000000..6e1f701d --- /dev/null +++ b/src/core/bell.c @@ -0,0 +1,258 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* Metacity visual bell */ + +/* + * Copyright (C) 2002 Sun Microsystems Inc. + * Copyright (C) 2005, 2006 Elijah Newren + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <config.h> +#include "bell.h" +#include "screen.h" +#include "prefs.h" + +static void +meta_bell_flash_screen (MetaDisplay *display, + MetaScreen *screen) +{ + Window root = screen->xroot; + int width = screen->rect.width; + int height = screen->rect.height; + + if (screen->flash_window == None) + { + Visual *visual = (Visual *)CopyFromParent; + XSetWindowAttributes xswa; + int depth = CopyFromParent; + xswa.save_under = True; + xswa.override_redirect = True; + /* + * TODO: use XGetVisualInfo and determine which is an + * overlay, if one is present, and use the Overlay visual + * for this window (for performance reasons). + * Not sure how to tell this yet... + */ + screen->flash_window = XCreateWindow (display->xdisplay, root, + 0, 0, width, height, + 0, depth, + InputOutput, + visual, + /* note: XSun doesn't like SaveUnder here */ + CWSaveUnder | CWOverrideRedirect, + &xswa); + XSelectInput (display->xdisplay, screen->flash_window, ExposureMask); + XMapWindow (display->xdisplay, screen->flash_window); + XSync (display->xdisplay, False); + XFlush (display->xdisplay); + XUnmapWindow (display->xdisplay, screen->flash_window); + } + else + { + /* just draw something in the window */ + GC gc = XCreateGC (display->xdisplay, screen->flash_window, 0, NULL); + XMapWindow (display->xdisplay, screen->flash_window); + XSetForeground (display->xdisplay, gc, + WhitePixel (display->xdisplay, + XScreenNumberOfScreen (screen->xscreen))); + XFillRectangle (display->xdisplay, screen->flash_window, gc, + 0, 0, width, height); + XSetForeground (display->xdisplay, gc, + BlackPixel (display->xdisplay, + XScreenNumberOfScreen (screen->xscreen))); + XFillRectangle (display->xdisplay, screen->flash_window, gc, + 0, 0, width, height); + XFlush (display->xdisplay); + XSync (display->xdisplay, False); + XUnmapWindow (display->xdisplay, screen->flash_window); + XFreeGC (display->xdisplay, gc); + } + + if (meta_prefs_get_focus_mode () != META_FOCUS_MODE_CLICK && + !display->mouse_mode) + meta_display_increment_focus_sentinel (display); + XFlush (display->xdisplay); +} + +#ifdef HAVE_XKB +static void +meta_bell_flash_fullscreen (MetaDisplay *display, + XkbAnyEvent *xkb_ev) +{ + XkbBellNotifyEvent *xkb_bell_ev = (XkbBellNotifyEvent *) xkb_ev; + MetaScreen *screen; + + g_assert (xkb_ev->xkb_type == XkbBellNotify); + if (xkb_bell_ev->window != None) + { + screen = meta_display_screen_for_xwindow (display, xkb_bell_ev->window); + if (screen) + meta_bell_flash_screen (display, screen); + } + else + { + GSList *screen_list = display->screens; + while (screen_list) + { + screen = (MetaScreen *) screen_list->data; + meta_bell_flash_screen (display, screen); + screen_list = screen_list->next; + } + } +} + +static gboolean +meta_bell_unflash_frame (gpointer data) +{ + MetaFrame *frame = (MetaFrame *) data; + frame->is_flashing = 0; + meta_frame_queue_draw (frame); + return FALSE; +} + +static void +meta_bell_flash_window_frame (MetaWindow *window) +{ + g_assert (window->frame != NULL); + window->frame->is_flashing = 1; + meta_frame_queue_draw (window->frame); + g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, 100, + meta_bell_unflash_frame, window->frame, NULL); +} + +static void +meta_bell_flash_frame (MetaDisplay *display, + XkbAnyEvent *xkb_ev) +{ + XkbBellNotifyEvent *xkb_bell_event = (XkbBellNotifyEvent *) xkb_ev; + MetaWindow *window; + + g_assert (xkb_ev->xkb_type == XkbBellNotify); + window = meta_display_lookup_x_window (display, xkb_bell_event->window); + if (!window && (display->focus_window) && (display->focus_window->frame)) + { + window = display->focus_window; + } + if (window) + { + meta_bell_flash_window_frame (window); + } + else /* revert to fullscreen flash if there's no focussed window */ + { + meta_bell_flash_fullscreen (display, xkb_ev); + } +} + +static void +meta_bell_visual_notify (MetaDisplay *display, + XkbAnyEvent *xkb_ev) +{ + switch (meta_prefs_get_visual_bell_type ()) + { + case META_VISUAL_BELL_FULLSCREEN_FLASH: + meta_bell_flash_fullscreen (display, xkb_ev); + break; + case META_VISUAL_BELL_FRAME_FLASH: + meta_bell_flash_frame (display, xkb_ev); /* does nothing yet */ + break; + case META_VISUAL_BELL_INVALID: + /* do nothing */ + break; + } +} + +void +meta_bell_notify (MetaDisplay *display, + XkbAnyEvent *xkb_ev) +{ + /* flash something */ + if (meta_prefs_get_visual_bell ()) + meta_bell_visual_notify (display, xkb_ev); +} +#endif + +void +meta_bell_set_audible (MetaDisplay *display, gboolean audible) +{ +#ifdef HAVE_XKB + XkbChangeEnabledControls (display->xdisplay, + XkbUseCoreKbd, + XkbAudibleBellMask, + audible ? XkbAudibleBellMask : 0); +#endif +} + +gboolean +meta_bell_init (MetaDisplay *display) +{ +#ifdef HAVE_XKB + int xkb_base_error_type, xkb_opcode; + + if (!XkbQueryExtension (display->xdisplay, &xkb_opcode, + &display->xkb_base_event_type, + &xkb_base_error_type, + NULL, NULL)) + { + display->xkb_base_event_type = -1; + g_message ("could not find XKB extension."); + return FALSE; + } + else + { + unsigned int mask = XkbBellNotifyMask; + gboolean visual_bell_auto_reset = FALSE; + /* TRUE if and when non-broken version is available */ + XkbSelectEvents (display->xdisplay, + XkbUseCoreKbd, + XkbBellNotifyMask, + XkbBellNotifyMask); + XkbChangeEnabledControls (display->xdisplay, + XkbUseCoreKbd, + XkbAudibleBellMask, + meta_prefs_bell_is_audible () + ? XkbAudibleBellMask : 0); + if (visual_bell_auto_reset) { + XkbSetAutoResetControls (display->xdisplay, + XkbAudibleBellMask, + &mask, + &mask); + } + return TRUE; + } +#endif + return FALSE; +} + +void +meta_bell_shutdown (MetaDisplay *display) +{ +#ifdef HAVE_XKB + /* TODO: persist initial bell state in display, reset here */ + XkbChangeEnabledControls (display->xdisplay, + XkbUseCoreKbd, + XkbAudibleBellMask, + XkbAudibleBellMask); +#endif +} + +void +meta_bell_notify_frame_destroy (MetaFrame *frame) +{ + if (frame->is_flashing) + g_source_remove_by_funcs_user_data (&g_timeout_funcs, frame); +} |