Commit e60da6c0 authored by Rob Adams's avatar Rob Adams Committed by Rob Adams

Fix some support for EWMH hints, and fix USER_TIME support to include the

2004-07-31  Rob Adams  <readams@readams.net>

	Fix some support for EWMH hints, and fix USER_TIME support to
	include the DEMANDS_ATTENTION hint.  Also includes some code for
	implementing _NET_RESTACK_WINDOW and _NET_MOVERESIZE_WINDOW, but
	this is disabled pending feature thaw.

	* COMPLIANCE: update with new information

	* src/display.c (meta_display_open): add new hints to list

	* src/display.h (_MetaDisplay): Add new atoms to struct

	* src/screen.c (set_supported_hint): update the list of support
	hints.
	(set_desktop_viewport_hint): new function sets the viewport hint
	to (0,0) as required by the spec for WMs with no viewport support.
	(set_desktop_geometry_hint): new function to set the desktop size
	hint to the size of the display, since we don't implement large
	desktop support, as required by the spec.
	(meta_screen_resize): update the geometry hint on screen resize

	* src/window.c (meta_window_new_with_attrs): Initialize
	demands_attention state
	(set_net_wm_state): Set demands_attention hint in the window state
	(meta_window_show): If we don't pop up a window because of
	USER_TIME, set DEMANDS_ATTENTION on the window.
	(meta_window_focus): When a window receives focus, remove
	DEMANDS_ATTENTION hint
	(meta_window_client_message): Allow other apps to set
	DEMANDS_ATTENTION on a window.  Also, if the _NET_ACTIVE_WINDOW
	hint includes a timestamp, use it.
	(update_net_wm_state): Read DEMANDS_ATTENTION state also

	* src/window.h (_MetaWindow): add wm_state_demands_attention bit.
parent c2bbd8b6
...@@ -48,15 +48,12 @@ standard is available at http://freedesktop.org/Standards/wm-spec/ ...@@ -48,15 +48,12 @@ standard is available at http://freedesktop.org/Standards/wm-spec/
+ _NET_NUMBER_OF_DESKTOPS (1.3) + _NET_NUMBER_OF_DESKTOPS (1.3)
- _NET_DESKTOP_GEOMETRY (-) + _NET_DESKTOP_GEOMETRY (1.3)
Metacity does not implement large desktops. Regardless, according Metacity does not implement large desktops, so this is kept set to
to the specification, metacity SHOULD set this property to the the screen size.
screen size, and update it if the screen size changes because of a
RandR change.
- _NET_DESKTOP_VIEWPORT (-) + _NET_DESKTOP_VIEWPORT (1.3)
Metacity does not implement viewports. However, according to the Metacity does not implement viewports, so this is a constant (0,0).
specification, metacity MUST set this property to (0,0)
+ _NET_CURRENT_DESKTOP (1.3) + _NET_CURRENT_DESKTOP (1.3)
...@@ -82,15 +79,16 @@ standard is available at http://freedesktop.org/Standards/wm-spec/ ...@@ -82,15 +79,16 @@ standard is available at http://freedesktop.org/Standards/wm-spec/
+ _NET_CLOSE_WINDOW (1.3) + _NET_CLOSE_WINDOW (1.3)
- _NET_MOVERESIZE_WINDOW (-) - _NET_MOVERESIZE_WINDOW (1.3)
Metacity does not support this message. The specification states Metacity supports this message, but the specification is unclear on
that metacity should treat this message like a ConfigureRequest. the layout of the detail value, and as such it is #if 0'd in the code
Not hard to implement; just hasn't been done.
+ _NET_WM_MOVERESIZE (1.3) + _NET_WM_MOVERESIZE (1.3)
- _NET_RESTACK_WINDOW (-) - _NET_RESTACK_WINDOW (1.3)
Metacity does not currently support this message. Metacity will raise or lower windows in response to this message,
but the sibling restack modes are not supported, and it is currently
#if 0'd in the code.
+ _NET_REQUEST_FRAME_EXTENTS (1.3) + _NET_REQUEST_FRAME_EXTENTS (1.3)
...@@ -114,14 +112,14 @@ standard is available at http://freedesktop.org/Standards/wm-spec/ ...@@ -114,14 +112,14 @@ standard is available at http://freedesktop.org/Standards/wm-spec/
+ _NET_WM_WINDOW_TYPE (1.3) + _NET_WM_WINDOW_TYPE (1.3)
/ _NET_WM_STATE (1.3) / _NET_WM_STATE (1.3)
This property is read and updated according to the specification,
but see caveat below.
Metacity does not recognize separate vertical and horizontal Metacity does not recognize separate vertical and horizontal
maximization states. Currently metacity will do a two-dimensional maximization states. Currently metacity will do a two-dimensional
maximization if either property is set. maximization if either property is set.
See: http://bugzilla.gnome.org/show_bug.cgi?id=113601 See: http://bugzilla.gnome.org/show_bug.cgi?id=113601
Metacity doesn't implement viewports so _NET_WM_STATE_STICKY is Metacity doesn't implement viewports so _NET_WM_STATE_STICKY is
unimplemented. unimplemented.
_NET_WM_STATE_DEMANDS_ATTENTION is neither read nor updated by
metacity. Metacity should unset it on window activation.
+ _NET_WM_ALLOWED_ACTIONS (1.3) + _NET_WM_ALLOWED_ACTIONS (1.3)
Metacity keeps this hint up to date. The code is somewhat crufty Metacity keeps this hint up to date. The code is somewhat crufty
...@@ -141,7 +139,7 @@ standard is available at http://freedesktop.org/Standards/wm-spec/ ...@@ -141,7 +139,7 @@ standard is available at http://freedesktop.org/Standards/wm-spec/
+ _NET_WM_HANDLED_ICONS (1.3) + _NET_WM_HANDLED_ICONS (1.3)
Metacity does not read or set this property. However, metacity Metacity does not read or set this property. However, metacity
never managed iconified windows, and so has no need to do so. never manages iconified windows, and so has no need to do so.
+ _NET_WM_USER_TIME (1.3) + _NET_WM_USER_TIME (1.3)
Metacity uses this property to prevent applications from stealing Metacity uses this property to prevent applications from stealing
......
2004-07-31 Rob Adams <readams@readams.net>
Fix some support for EWMH hints, and fix USER_TIME support to
include the DEMANDS_ATTENTION hint. Also includes some code for
implementing _NET_RESTACK_WINDOW and _NET_MOVERESIZE_WINDOW, but
this is disabled pending feature thaw.
* COMPLIANCE: update with new information
* src/display.c (meta_display_open): add new hints to list
* src/display.h (_MetaDisplay): Add new atoms to struct
* src/screen.c (set_supported_hint): update the list of support
hints.
(set_desktop_viewport_hint): new function sets the viewport hint
to (0,0) as required by the spec for WMs with no viewport support.
(set_desktop_geometry_hint): new function to set the desktop size
hint to the size of the display, since we don't implement large
desktop support, as required by the spec.
(meta_screen_resize): update the geometry hint on screen resize
* src/window.c (meta_window_new_with_attrs): Initialize
demands_attention state
(set_net_wm_state): Set demands_attention hint in the window state
(meta_window_show): If we don't pop up a window because of
USER_TIME, set DEMANDS_ATTENTION on the window.
(meta_window_focus): When a window receives focus, remove
DEMANDS_ATTENTION hint
(meta_window_client_message): Allow other apps to set
DEMANDS_ATTENTION on a window. Also, if the _NET_ACTIVE_WINDOW
hint includes a timestamp, use it.
(update_net_wm_state): Read DEMANDS_ATTENTION state also
* src/window.h (_MetaWindow): add wm_state_demands_attention bit.
2004-07-22 Rob Adams <readams@readams.net> 2004-07-22 Rob Adams <readams@readams.net>
* src/metacity.schemas.in: Add trailing quotes to keybinding * src/metacity.schemas.in: Add trailing quotes to keybinding
......
...@@ -282,6 +282,11 @@ meta_display_open (const char *name) ...@@ -282,6 +282,11 @@ meta_display_open (const char *name)
"_NET_FRAME_EXTENTS", "_NET_FRAME_EXTENTS",
"_NET_REQUEST_FRAME_EXTENTS", "_NET_REQUEST_FRAME_EXTENTS",
"_NET_WM_USER_TIME", "_NET_WM_USER_TIME",
"_NET_WM_STATE_DEMANDS_ATTENTION",
"_NET_RESTACK_WINDOW",
"_NET_MOVERESIZE_WINDOW",
"_NET_DESKTOP_GEOMETRY",
"_NET_DESKTOP_VIEWPORT"
}; };
Atom atoms[G_N_ELEMENTS(atom_names)]; Atom atoms[G_N_ELEMENTS(atom_names)];
...@@ -429,6 +434,11 @@ meta_display_open (const char *name) ...@@ -429,6 +434,11 @@ meta_display_open (const char *name)
display->atom_net_frame_extents = atoms[83]; display->atom_net_frame_extents = atoms[83];
display->atom_net_request_frame_extents = atoms[84]; display->atom_net_request_frame_extents = atoms[84];
display->atom_net_wm_user_time = atoms[85]; display->atom_net_wm_user_time = atoms[85];
display->atom_net_wm_state_demands_attention = atoms[86];
display->atom_net_restack_window = atoms[87];
display->atom_net_moveresize_window = atoms[88];
display->atom_net_desktop_geometry = atoms[89];
display->atom_net_desktop_viewport = atoms[90];
display->prop_hooks = NULL; display->prop_hooks = NULL;
meta_display_init_window_prop_hooks (display); meta_display_init_window_prop_hooks (display);
...@@ -1170,6 +1180,107 @@ double_click_timeout_for_event (MetaDisplay *display, ...@@ -1170,6 +1180,107 @@ double_click_timeout_for_event (MetaDisplay *display,
return meta_ui_get_double_click_timeout (screen->ui); return meta_ui_get_double_click_timeout (screen->ui);
} }
#if 0
static void
handle_net_moveresize_window (MetaDisplay* display,
XEvent *event)
{
MetaWindow *window;
int x, y, width, height;
gboolean only_resize;
unsigned int gravity;
unsigned int mode;
window = meta_display_lookup_x_window (display,
event->xclient.window);
/*
* FIXME: The specification seems to have serious endian issues
* here. Does bits 8-11 mean the high-order byte, or the low-order
* byte?
*/
gravity = (event->xclient.data.l[0] & ~0xff);
mode = (event->xclient.data.l[0] & ~0xff00) >> 8;
if (window)
{
meta_window_get_gravity_position (window, &x, &y);
width = window->rect.width;
height = window->rect.height;
if (mode & (CWX | CWY))
only_resize = FALSE;
else
only_resize = TRUE;
if (mode & CWX)
x = event->xclient.data.l[1];
if (mode & CWY)
y = event->xclient.data.l[2];
if (mode & CWWidth)
width = event->xclient.data.l[3];
if (mode & CWHeight)
height = event->xclient.data.l[4];
if (only_resize)
{
if (gravity)
meta_window_resize_with_gravity (window,
TRUE,
width,
height,
gravity);
else
meta_window_resize (window,
TRUE,
width,
height);
}
else
{
meta_window_move_resize (window,
TRUE,
x,
y,
width,
height);
}
}
}
static void
handle_net_restack_window (MetaDisplay* display,
XEvent *event)
{
MetaWindow *window;
window = meta_display_lookup_x_window (display,
event->xclient.window);
if (window)
{
/*
* The EWMH includes a sibling for the restack request, but we
* don't currently support these types of raises.
*
*/
switch (event->xclient.data.l[2])
{
case Above:
meta_window_raise (window);
break;
case Below:
meta_window_lower (window);
break;
case TopIf:
case BottomIf:
case Opposite:
break;
}
}
}
#endif
static gboolean static gboolean
event_callback (XEvent *event, event_callback (XEvent *event,
gpointer data) gpointer data)
...@@ -1873,10 +1984,18 @@ event_callback (XEvent *event, ...@@ -1873,10 +1984,18 @@ event_callback (XEvent *event,
else if (event->xproperty.atom == else if (event->xproperty.atom ==
display->atom_net_desktop_names) display->atom_net_desktop_names)
meta_screen_update_workspace_names (screen); meta_screen_update_workspace_names (screen);
#if 0
else if (event->xproperty.atom ==
display->atom_net_restack_window)
handle_net_restack_window (display, event);
else if (event->xproperty.atom ==
display->atom_net_moveresize_window)
handle_net_moveresize_window (display, event);
#endif
/* we just use this property as a sentinel to avoid /* we just use this property as a sentinel to avoid
* certain race conditions. See the comment for the * certain race conditions. See the comment for the
* sentinel_counter variable declaration in display.h * sentinel_counter variable declaration in display.h
*/ */
if (event->xproperty.atom == if (event->xproperty.atom ==
display->atom_metacity_sentinel) display->atom_metacity_sentinel)
......
...@@ -176,6 +176,11 @@ struct _MetaDisplay ...@@ -176,6 +176,11 @@ struct _MetaDisplay
Atom atom_net_frame_extents; Atom atom_net_frame_extents;
Atom atom_net_request_frame_extents; Atom atom_net_request_frame_extents;
Atom atom_net_wm_user_time; Atom atom_net_wm_user_time;
Atom atom_net_wm_state_demands_attention;
Atom atom_net_restack_window;
Atom atom_net_moveresize_window;
Atom atom_net_desktop_geometry;
Atom atom_net_desktop_viewport;
/* This is the actual window from focus events, /* This is the actual window from focus events,
* not the one we last set * not the one we last set
......
...@@ -57,6 +57,9 @@ static void set_workspace_names (MetaScreen *screen); ...@@ -57,6 +57,9 @@ static void set_workspace_names (MetaScreen *screen);
static void prefs_changed_callback (MetaPreference pref, static void prefs_changed_callback (MetaPreference pref,
gpointer data); gpointer data);
static void set_desktop_geometry_hint (MetaScreen *screen);
static void set_desktop_viewport_hint (MetaScreen *screen);
#ifdef HAVE_STARTUP_NOTIFICATION #ifdef HAVE_STARTUP_NOTIFICATION
static void meta_screen_sn_event (SnMonitorEvent *event, static void meta_screen_sn_event (SnMonitorEvent *event,
void *user_data); void *user_data);
...@@ -82,7 +85,7 @@ set_wm_check_hint (MetaScreen *screen) ...@@ -82,7 +85,7 @@ set_wm_check_hint (MetaScreen *screen)
static int static int
set_supported_hint (MetaScreen *screen) set_supported_hint (MetaScreen *screen)
{ {
#define N_SUPPORTED 54 #define N_SUPPORTED 58
Atom atoms[N_SUPPORTED]; Atom atoms[N_SUPPORTED];
atoms[0] = screen->display->atom_net_wm_name; atoms[0] = screen->display->atom_net_wm_name;
...@@ -139,6 +142,12 @@ set_supported_hint (MetaScreen *screen) ...@@ -139,6 +142,12 @@ set_supported_hint (MetaScreen *screen)
atoms[51] = screen->display->atom_net_wm_action_minimize; atoms[51] = screen->display->atom_net_wm_action_minimize;
atoms[52] = screen->display->atom_net_frame_extents; atoms[52] = screen->display->atom_net_frame_extents;
atoms[53] = screen->display->atom_net_request_frame_extents; atoms[53] = screen->display->atom_net_request_frame_extents;
atoms[54] = screen->display->atom_net_wm_user_time;
atoms[55] = screen->display->atom_net_wm_state_demands_attention;
atoms[56] = screen->display->atom_net_desktop_geometry;
atoms[57] = screen->display->atom_net_desktop_viewport;
//atoms[58] = screen->display->atom_net_restack_window;
//atoms[59] = screen->display->atom_net_moveresize_window;
XChangeProperty (screen->display->xdisplay, screen->xroot, XChangeProperty (screen->display->xdisplay, screen->xroot,
screen->display->atom_net_supported, screen->display->atom_net_supported,
...@@ -575,6 +584,10 @@ meta_screen_new (MetaDisplay *display, ...@@ -575,6 +584,10 @@ meta_screen_new (MetaDisplay *display,
set_wm_check_hint (screen); set_wm_check_hint (screen);
set_desktop_viewport_hint (screen);
set_desktop_geometry_hint (screen);
meta_screen_update_workspace_layout (screen); meta_screen_update_workspace_layout (screen);
/* Get current workspace */ /* Get current workspace */
...@@ -965,6 +978,51 @@ set_number_of_spaces_hint (MetaScreen *screen, ...@@ -965,6 +978,51 @@ set_number_of_spaces_hint (MetaScreen *screen,
meta_error_trap_pop (screen->display, FALSE); meta_error_trap_pop (screen->display, FALSE);
} }
static void
set_desktop_geometry_hint (MetaScreen *screen)
{
unsigned long data[2];
if (screen->closing > 0)
return;
data[0] = screen->width;
data[1] = screen->height;
meta_verbose ("Setting _NET_DESKTOP_GEOMETRY to %ld, %ld\n", data[0], data[1]);
meta_error_trap_push (screen->display);
XChangeProperty (screen->display->xdisplay, screen->xroot,
screen->display->atom_net_desktop_geometry,
XA_CARDINAL,
32, PropModeReplace, (guchar*) data, 2);
meta_error_trap_pop (screen->display, FALSE);
}
static void
set_desktop_viewport_hint (MetaScreen *screen)
{
unsigned long data[2];
if (screen->closing > 0)
return;
/*
* Metacity does not implement viewports, so this is a fixed 0,0
*/
data[0] = 0;
data[1] = 0;
meta_verbose ("Setting _NET_DESKTOP_VIEWPORT to 0, 0\n");
meta_error_trap_push (screen->display);
XChangeProperty (screen->display->xdisplay, screen->xroot,
screen->display->atom_net_desktop_viewport,
XA_CARDINAL,
32, PropModeReplace, (guchar*) data, 2);
meta_error_trap_pop (screen->display, FALSE);
}
static void static void
update_num_workspaces (MetaScreen *screen) update_num_workspaces (MetaScreen *screen)
{ {
...@@ -2101,6 +2159,7 @@ meta_screen_resize (MetaScreen *screen, ...@@ -2101,6 +2159,7 @@ meta_screen_resize (MetaScreen *screen,
screen->height = height; screen->height = height;
reload_xinerama_infos (screen); reload_xinerama_infos (screen);
set_desktop_geometry_hint (screen);
/* Queue a resize on all the windows */ /* Queue a resize on all the windows */
meta_screen_foreach_window (screen, meta_screen_resize_func, 0); meta_screen_foreach_window (screen, meta_screen_resize_func, 0);
......
...@@ -491,6 +491,7 @@ meta_window_new_with_attrs (MetaDisplay *display, ...@@ -491,6 +491,7 @@ meta_window_new_with_attrs (MetaDisplay *display,
window->wm_state_skip_pager = FALSE; window->wm_state_skip_pager = FALSE;
window->wm_state_above = FALSE; window->wm_state_above = FALSE;
window->wm_state_below = FALSE; window->wm_state_below = FALSE;
window->wm_state_demands_attention = FALSE;
window->res_class = NULL; window->res_class = NULL;
window->res_name = NULL; window->res_name = NULL;
...@@ -1125,7 +1126,7 @@ static void ...@@ -1125,7 +1126,7 @@ static void
set_net_wm_state (MetaWindow *window) set_net_wm_state (MetaWindow *window)
{ {
int i; int i;
unsigned long data[10]; unsigned long data[11];
i = 0; i = 0;
if (window->shaded) if (window->shaded)
...@@ -1175,7 +1176,12 @@ set_net_wm_state (MetaWindow *window) ...@@ -1175,7 +1176,12 @@ set_net_wm_state (MetaWindow *window)
data[i] = window->display->atom_net_wm_state_below; data[i] = window->display->atom_net_wm_state_below;
++i; ++i;
} }
if (window->wm_state_demands_attention)
{
data[i] = window->display->atom_net_wm_state_demands_attention;
++i;
}
meta_verbose ("Setting _NET_WM_STATE with %d atoms\n", i); meta_verbose ("Setting _NET_WM_STATE with %d atoms\n", i);
meta_error_trap_push (window->display); meta_error_trap_push (window->display);
...@@ -1810,12 +1816,14 @@ meta_window_show (MetaWindow *window) ...@@ -1810,12 +1816,14 @@ meta_window_show (MetaWindow *window)
meta_window_focus (window, meta_window_focus (window,
meta_display_get_current_time (window->display)); meta_display_get_current_time (window->display));
} }
else
window->wm_state_demands_attention = TRUE;
} }
if (did_show) if (did_show)
{ {
set_net_wm_state (window); set_net_wm_state (window);
if (window->struts) if (window->struts)
{ {
meta_topic (META_DEBUG_WORKAREA, meta_topic (META_DEBUG_WORKAREA,
...@@ -3266,7 +3274,7 @@ meta_window_focus (MetaWindow *window, ...@@ -3266,7 +3274,7 @@ meta_window_focus (MetaWindow *window,
window->desc); window->desc);
return; return;
} }
/* For output-only or shaded windows, focus the frame. /* For output-only or shaded windows, focus the frame.
* This seems to result in the client window getting key events * This seems to result in the client window getting key events
* though, so I don't know if it's icccm-compliant. * though, so I don't know if it's icccm-compliant.
...@@ -3317,6 +3325,12 @@ meta_window_focus (MetaWindow *window, ...@@ -3317,6 +3325,12 @@ meta_window_focus (MetaWindow *window,
meta_error_trap_pop (window->display, FALSE); meta_error_trap_pop (window->display, FALSE);
} }
if (window->wm_state_demands_attention)
{
window->wm_state_demands_attention = FALSE;
set_net_wm_state (window);
}
} }
static void static void
...@@ -3956,6 +3970,16 @@ meta_window_client_message (MetaWindow *window, ...@@ -3956,6 +3970,16 @@ meta_window_client_message (MetaWindow *window,
meta_window_update_layer (window); meta_window_update_layer (window);
set_net_wm_state (window); set_net_wm_state (window);
} }
if (first == display->atom_net_wm_state_demands_attention ||
second == display->atom_net_wm_state_demands_attention)
{
window->wm_state_demands_attention =
(action == _NET_WM_STATE_ADD) ||
(action == _NET_WM_STATE_TOGGLE && !window->wm_state_demands_attention);
set_net_wm_state (window);
}
return TRUE; return TRUE;
} }
...@@ -4100,8 +4124,22 @@ meta_window_client_message (MetaWindow *window, ...@@ -4100,8 +4124,22 @@ meta_window_client_message (MetaWindow *window,
meta_verbose ("_NET_ACTIVE_WINDOW request for window '%s', activating", meta_verbose ("_NET_ACTIVE_WINDOW request for window '%s', activating",
window->desc); window->desc);
meta_window_activate (window, meta_display_get_current_time (window->display)); if (event->xclient.data.l[0] != 0)
{
/* Client supports newer _NET_ACTIVE_WINDOW with a
* convenient timestamp
*/
meta_window_activate (window,
event->xclient.data.l[1]);
}
else
{
/* Client using older EWMH _NET_ACTIVE_WINDOW without a
* timestamp
*/
meta_window_activate (window, meta_display_get_current_time (window->display));
}
return TRUE; return TRUE;
} }
...@@ -4568,6 +4606,7 @@ update_net_wm_state (MetaWindow *window) ...@@ -4568,6 +4606,7 @@ update_net_wm_state (MetaWindow *window)
window->wm_state_skip_pager = FALSE; window->wm_state_skip_pager = FALSE;
window->wm_state_above = FALSE; window->wm_state_above = FALSE;
window->wm_state_below = FALSE; window->wm_state_below = FALSE;
window->wm_state_demands_attention = FALSE;
if (meta_prop_get_atom_list (window->display, window->xwindow, if (meta_prop_get_atom_list (window->display, window->xwindow,
window->display->atom_net_wm_state, window->display->atom_net_wm_state,
...@@ -4596,6 +4635,8 @@ update_net_wm_state (MetaWindow *window) ...@@ -4596,6 +4635,8 @@ update_net_wm_state (MetaWindow *window)
window->wm_state_above = TRUE; window->wm_state_above = TRUE;
else if (atoms[i] == window->display->atom_net_wm_state_below) else if (atoms[i] == window->display->atom_net_wm_state_below)
window->wm_state_below = TRUE; window->wm_state_below = TRUE;
else if (atoms[i] == window->display->atom_net_wm_state_demands_attention)
window->wm_state_demands_attention = TRUE;
++i; ++i;
} }
......
...@@ -190,6 +190,9 @@ struct _MetaWindow ...@@ -190,6 +190,9 @@ struct _MetaWindow
/* TRUE if client set these */ /* TRUE if client set these */
guint wm_state_above : 1; guint wm_state_above : 1;
guint wm_state_below : 1; guint wm_state_below : 1;
/* EWHH demands attention flag */
guint wm_state_demands_attention : 1;
/* this flag tracks receipt of focus_in focus_out and /* this flag tracks receipt of focus_in focus_out and
* determines whether we draw the focus * determines whether we draw the focus
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment