diff options
| author | Perberos <[email protected]> | 2011-12-01 23:52:01 -0300 | 
|---|---|---|
| committer | Perberos <[email protected]> | 2011-12-01 23:52:01 -0300 | 
| commit | 28a029a4990d2a84f9d6a0b890eba812ea503998 (patch) | |
| tree | 7a69477d0dd6bf351801fa9698d95224e4fe47b6 /src/core/bell.c | |
| download | marco-28a029a4990d2a84f9d6a0b890eba812ea503998.tar.bz2 marco-28a029a4990d2a84f9d6a0b890eba812ea503998.tar.xz | |
moving from https://github.com/perberos/mate-desktop-environment
Diffstat (limited to 'src/core/bell.c')
| -rw-r--r-- | src/core/bell.c | 397 | 
1 files changed, 397 insertions, 0 deletions
| diff --git a/src/core/bell.c b/src/core/bell.c new file mode 100644 index 00000000..560b3569 --- /dev/null +++ b/src/core/bell.c @@ -0,0 +1,397 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* Marco 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. + */ + +/** + * \file bell.c Ring the bell or flash the screen + * + * Sometimes, X programs "ring the bell", whatever that means. Marco lets + * the user configure the bell to be audible or visible (aka visual), and + * if it's visual it can be configured to be frame-flash or fullscreen-flash. + * We never get told about audible bells; X handles them just fine by itself. + * + * Visual bells come in at meta_bell_notify(), which checks we are actually + * in visual mode and calls through to bell_visual_notify(). That + * function then checks what kind of visual flash you like, and calls either + * bell_flash_fullscreen()-- which calls bell_flash_screen() to do + * its work-- or bell_flash_frame(), which flashes the focussed window + * using bell_flash_window_frame(), unless there is no such window, in + * which case it flashes the screen instead. bell_flash_window_frame() + * flashes the frame and calls bell_unflash_frame() as a timeout to + * remove the flash. + * + * The visual bell was the result of a discussion in Bugzilla here: + * <http://bugzilla.gnome.org/show_bug.cgi?id=99886>. + * + * Several of the functions in this file are ifdeffed out entirely if we are + * found not to have the XKB extension, which is required to do these clever + * things with bells; some others are entirely no-ops in that case. + */ + +#include <config.h> +#include "bell.h" +#include "screen-private.h" +#include "prefs.h" +#include <canberra-gtk.h> + +/** + * Flashes one entire screen.  This is done by making a window the size of the + * whole screen (or reusing the old one, if it's still around), mapping it, + * painting it white and then black, and then unmapping it. We set saveunder so + * that all the windows behind it come back immediately. + * + * Unlike frame flashes, we don't do fullscreen flashes with a timeout; rather, + * we do them in one go, because we don't have to rely on the theme code + * redrawing the frame for us in order to do the flash. + * + * \param display  The display which owns the screen (rather redundant) + * \param screen   The screen to flash + * + * \bug The way I read it, this appears not to do the flash + * the first time we flash a particular display. Am I wrong? + * + * \bug This appears to destroy our current XSync status. + */ +static void +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); +} + +/** + * Flashes one screen, or all screens, in response to a bell event. + * If the event is on a particular window, flash the screen that + * window is on. Otherwise, flash every screen on this display. + * + * If the configure script found we had no XKB, this does not exist. + * + * \param display  The display the event came in on + * \param xkb_ev   The bell event + */ +#ifdef HAVE_XKB +static void +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) +	bell_flash_screen (display, screen); +    } +  else +    { +      GSList *screen_list = display->screens; +      while (screen_list) +	{ +	  screen = (MetaScreen *) screen_list->data; +	  bell_flash_screen (display, screen); +	  screen_list = screen_list->next; +	} +    } +} + +/** + * Makes a frame be not flashed; this is the timeout half of + * bell_flash_window_frame(). This is done simply by clearing the + * flash flag and queuing a redraw of the frame. + * + * If the configure script found we had no XKB, this does not exist. + * + * \param data  The frame to unflash, cast to a gpointer so it can go into + *              a callback function. + * \return Always FALSE, so we don't get called again. + * + * \bug This is the parallel to bell_flash_window_frame(), so it should + * really be called meta_bell_unflash_window_frame(). + */ +static gboolean +bell_unflash_frame (gpointer data) +{ +  MetaFrame *frame = (MetaFrame *) data; +  frame->is_flashing = 0; +  meta_frame_queue_draw (frame); +  return FALSE; +} + +/** + * Makes a frame flash and then return to normal shortly afterwards. + * This is done by setting a flag so that the theme + * code will temporarily draw the frame as focussed if it's unfocussed and + * vice versa, and then queueing a redraw. Lastly, we create a timeout so + * that the flag can be unset and the frame re-redrawn. + * + * If the configure script found we had no XKB, this does not exist. + * + * \param window  The window to flash + */ +static void +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, +      bell_unflash_frame, window->frame, NULL); +} + +/** + * Flashes the frame of the focussed window. If there is no focussed window, + * flashes the screen. + * + * \param display  The display the bell event came in on + * \param xkb_ev   The bell event we just received + */ +static void +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)) +    { +      window = display->focus_window; +    } +  if (window && window->frame) +    { +      bell_flash_window_frame (window); +    } +  else /* revert to fullscreen flash if there's no focussed window */ +    { +      bell_flash_fullscreen (display, xkb_ev); +    } +} + +/** + * Gives the user some kind of visual bell substitute, in response to a + * bell event. What this is depends on the "visual bell type" pref. + * + * If the configure script found we had no XKB, this does not exist. + * + * \param display  The display the bell event came in on + * \param xkb_ev   The bell event we just received + * + * \bug This should be merged with meta_bell_notify(). + */ +static void +bell_visual_notify (MetaDisplay *display, +			 XkbAnyEvent *xkb_ev) +{ +  switch (meta_prefs_get_visual_bell_type ()) +    { +    case META_VISUAL_BELL_FULLSCREEN_FLASH: +      bell_flash_fullscreen (display, xkb_ev); +      break; +    case META_VISUAL_BELL_FRAME_FLASH: +      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 ()) +    bell_visual_notify (display, xkb_ev); + +  if (meta_prefs_bell_is_audible ()) +    { +      ca_proplist *p; +      XkbBellNotifyEvent *xkb_bell_event = (XkbBellNotifyEvent*) xkb_ev; +      MetaWindow *window; +      int res; + +      ca_proplist_create (&p); +      ca_proplist_sets (p, CA_PROP_EVENT_ID, "bell-window-system"); +      ca_proplist_sets (p, CA_PROP_EVENT_DESCRIPTION, _("Bell event")); +      ca_proplist_sets (p, CA_PROP_CANBERRA_CACHE_CONTROL, "permanent"); + +      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) +        { +          ca_proplist_sets (p, CA_PROP_WINDOW_NAME, window->title); +          ca_proplist_setf (p, CA_PROP_WINDOW_X11_XID, "%lu", (unsigned long)window->xwindow); +          ca_proplist_sets (p, CA_PROP_APPLICATION_NAME, window->res_name); +          ca_proplist_setf (p, CA_PROP_APPLICATION_PROCESS_ID, "%d", window->net_wm_pid); +        } + +      /* First, we try to play a real sound ... */ +      res = ca_context_play_full (ca_gtk_context_get (), 1, p, NULL, NULL); + +      ca_proplist_destroy (p); + +      if (res != CA_SUCCESS && res != CA_ERROR_DISABLED) +        { +          /* ...and in case that failed we use the classic X11 bell. */ +          XkbForceDeviceBell (display->xdisplay, +                              xkb_bell_event->device, +                              xkb_bell_event->bell_class, +                              xkb_bell_event->bell_id, +                              xkb_bell_event->percent); +        } +    } +} +#endif /* HAVE_XKB */ + +void +meta_bell_set_audible (MetaDisplay *display, gboolean audible) +{ +} + +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, +                                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 +} + +/** + * Deals with a frame being destroyed. This is important because if we're + * using a visual bell, we might be flashing the edges of the frame, and + * so we'd have a timeout function waiting ready to un-flash them. If the + * frame's going away, we can tell the timeout not to bother. + * + * \param frame  The frame which is being destroyed + */ +void +meta_bell_notify_frame_destroy (MetaFrame *frame) +{ +  if (frame->is_flashing) +    g_source_remove_by_funcs_user_data (&g_timeout_funcs, frame); +} | 
