|
|
@ -51,312 +51,6 @@ |
|
|
|
|
|
|
|
#include <config.h> |
|
|
|
#include "bell.h" |
|
|
|
#include "screen-private.h" |
|
|
|
#include <meta/prefs.h> |
|
|
|
#ifdef HAVE_LIBCANBERRA |
|
|
|
#include <canberra-gtk.h> |
|
|
|
#endif |
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 () != C_DESKTOP_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) |
|
|
|
{ |
|
|
|
if (display->compositor) |
|
|
|
meta_compositor_flash_screen (display->compositor, screen); |
|
|
|
else |
|
|
|
bell_flash_screen (display, screen); |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
GSList *screen_list = display->screens; |
|
|
|
while (screen_list) |
|
|
|
{ |
|
|
|
screen = (MetaScreen *) screen_list->data; |
|
|
|
if (display->compositor) |
|
|
|
meta_compositor_flash_screen (display->compositor, screen); |
|
|
|
else |
|
|
|
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); |
|
|
|
/* Since this idle is added after the Clutter clock source, with
|
|
|
|
* the same priority, it will be executed after it as well, so |
|
|
|
* we are guaranteed to get at least one frame drawn in the |
|
|
|
* flashed state, no matter how loaded we are. |
|
|
|
*/ |
|
|
|
g_timeout_add_full (META_PRIORITY_REDRAW, 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 C_DESKTOP_VISUAL_BELL_FULLSCREEN_FLASH: |
|
|
|
bell_flash_fullscreen (display, xkb_ev); |
|
|
|
break; |
|
|
|
case C_DESKTOP_VISUAL_BELL_FRAME_FLASH: |
|
|
|
bell_flash_frame (display, xkb_ev); /* does nothing yet */ |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
LOCAL_SYMBOL void |
|
|
|
meta_bell_notify (MetaDisplay *display, |
|
|
|
XkbAnyEvent *xkb_ev) |
|
|
|
{ |
|
|
|
/* flash something */ |
|
|
|
if (meta_prefs_get_visual_bell ()) |
|
|
|
bell_visual_notify (display, xkb_ev); |
|
|
|
|
|
|
|
#ifdef HAVE_LIBCANBERRA |
|
|
|
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_LIBCANBERRA */ |
|
|
|
} |
|
|
|
#endif /* HAVE_XKB */ |
|
|
|
|
|
|
|
LOCAL_SYMBOL void |
|
|
|
meta_bell_set_audible (MetaDisplay *display, gboolean audible) |
|
|
|
{ |
|
|
|
#ifdef HAVE_XKB |
|
|
|
#ifdef HAVE_LIBCANBERRA |
|
|
|
/* When we are playing sounds using libcanberra support, we handle the
|
|
|
|
* bell whether its an audible bell or a visible bell */ |
|
|
|
gboolean enable_system_bell = FALSE; |
|
|
|
#else |
|
|
|
gboolean enable_system_bell = audible; |
|
|
|
#endif /* HAVE_LIBCANBERRA */ |
|
|
|
|
|
|
|
XkbChangeEnabledControls (display->xdisplay, |
|
|
|
XkbUseCoreKbd, |
|
|
|
XkbAudibleBellMask, |
|
|
|
enable_system_bell ? XkbAudibleBellMask : 0); |
|
|
|
#endif /* HAVE_XKB */ |
|
|
|
} |
|
|
|
|
|
|
|
LOCAL_SYMBOL gboolean |
|
|
|
meta_bell_init (MetaDisplay *display) |
|
|
@ -375,20 +69,10 @@ meta_bell_init (MetaDisplay *display) |
|
|
|
} |
|
|
|
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); |
|
|
|
meta_bell_set_audible (display, meta_prefs_bell_is_audible ()); |
|
|
|
if (visual_bell_auto_reset) { |
|
|
|
XkbSetAutoResetControls (display->xdisplay, |
|
|
|
XkbAudibleBellMask, |
|
|
|
&mask, |
|
|
|
&mask); |
|
|
|
} |
|
|
|
return TRUE; |
|
|
|
} |
|
|
|
#endif |
|
|
@ -407,17 +91,14 @@ meta_bell_shutdown (MetaDisplay *display) |
|
|
|
#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 |
|
|
|
*/ |
|
|
|
LOCAL_SYMBOL void |
|
|
|
meta_bell_notify_frame_destroy (MetaFrame *frame) |
|
|
|
meta_bell_notify (MetaDisplay *display, |
|
|
|
XkbAnyEvent *xkb_ev) |
|
|
|
{ |
|
|
|
if (frame->is_flashing) |
|
|
|
g_source_remove_by_funcs_user_data (&g_timeout_funcs, frame); |
|
|
|
} |
|
|
|
XkbBellNotifyEvent *xkb_bell_event = (XkbBellNotifyEvent *) xkb_ev; |
|
|
|
MetaWindow *bell_window = NULL; |
|
|
|
|
|
|
|
bell_window = meta_display_lookup_x_window (display, xkb_bell_event->window); |
|
|
|
|
|
|
|
g_signal_emit_by_name (display, "bell", bell_window); |
|
|
|
} |