simulate modal windows for badly behaved x11 clients
authorMike Blumenkrantz <zmike@osg.samsung.com>
Tue, 21 Jul 2015 22:24:39 +0000 (18:24 -0400)
committerMike Blumenkrantz <zmike@osg.samsung.com>
Tue, 21 Jul 2015 22:24:39 +0000 (18:24 -0400)
it seems that some clients, eg. libreoffice, don't set the modal window
property on child dialogs. instead of fighting for focus, set up the child
as a modal on the parent and then avoid the whole issue

fix T2594

src/bin/e_comp_x.c
src/bin/e_comp_x.h

index 7bf14db..e7bfc56 100644 (file)
@@ -19,6 +19,9 @@
  */
 #define MOVE_COUNTER_LIMIT 50
 
+#define PARENT_ACTIVATE_TIME 200
+#define PARENT_ACTIVATE_LIMIT 2
+
 EINTERN void _e_main_cb_x_fatal(void *data EINA_UNUSED);
 
 typedef struct _Frame_Extents Frame_Extents;
@@ -116,6 +119,24 @@ _e_comp_x_focus_check(void)
 }
 
 static void
+_e_comp_x_client_modal_setup(E_Client *ec)
+{
+   E_Comp_X_Client_Data *pcd;
+
+   ec->parent->modal = ec;
+   ec->parent->lock_close = 1;
+   pcd = _e_comp_x_client_data_get(ec->parent);
+   if (!pcd->lock_win)
+     {
+        eina_hash_add(clients_win_hash, &pcd->lock_win, ec->parent);
+        pcd->lock_win = ecore_x_window_input_new(e_client_util_pwin_get(ec->parent), 0, 0, ec->parent->w, ec->parent->h);
+        e_comp_ignore_win_add(E_PIXMAP_TYPE_X, pcd->lock_win);
+        ecore_x_window_show(pcd->lock_win);
+        ecore_x_icccm_name_class_set(pcd->lock_win, "comp_data->lock_win", "comp_data->lock_win");
+     }
+}
+
+static void
 _e_comp_x_client_frame_update(E_Client *ec, int l, int r, int t, int b)
 {
    ecore_x_netwm_frame_size_set(e_client_util_win_get(ec), l, r, t, b);
@@ -1980,7 +2001,31 @@ _e_comp_x_message(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_X_Event_Cl
                       (!ec->desk->visible))
                     e_client_urgent_set(ec, 1);
                   else
-                    e_client_activate(ec, EINA_TRUE);
+                    {
+                       /* some apps, eg. libreoffice, create "modal" windows which
+                        * do not have the netwm modal state set. instead, attempting to
+                        * focus the parent window results in the app immediately trying to
+                        * activate the "modal" window, triggering an infinite loop.
+                        *
+                        * by treating this "modal" window as a genuine modal window,
+                        * we (eventually) can ignore this type of bad behavior and
+                        * give the application/user the expected behavior
+                        */
+                       if (ec->parent && (!ec->parent->modal) && (e_client_focused_get() == ec->parent) &&
+                           (ev->time - focus_time < PARENT_ACTIVATE_TIME))
+                         {
+                            E_Comp_X_Client_Data *cd;
+
+                            cd = _e_comp_x_client_data_get(ec);
+                            if (cd)
+                              {
+                                 cd->parent_activate_count++;
+                                 if (cd->parent_activate_count >= PARENT_ACTIVATE_LIMIT)
+                                   _e_comp_x_client_modal_setup(ec);
+                              }
+                         }
+                       e_client_activate(ec, EINA_TRUE);
+                    }
                }
              else
                evas_object_raise(ec->frame);
@@ -3525,21 +3570,7 @@ _e_comp_x_hook_client_fetch(void *d EINA_UNUSED, E_Client *ec)
           {
              evas_object_layer_set(ec->frame, ec->parent->layer);
              if (ec->netwm.state.modal)
-               {
-                  E_Comp_X_Client_Data *pcd;
-
-                  ec->parent->modal = ec;
-                  ec->parent->lock_close = 1;
-                  pcd = _e_comp_x_client_data_get(ec->parent);
-                  if (!pcd->lock_win)
-                    {
-                       eina_hash_add(clients_win_hash, &pcd->lock_win, ec->parent);
-                       pcd->lock_win = ecore_x_window_input_new(e_client_util_pwin_get(ec->parent), 0, 0, ec->parent->w, ec->parent->h);
-                       e_comp_ignore_win_add(E_PIXMAP_TYPE_X, pcd->lock_win);
-                       ecore_x_window_show(pcd->lock_win);
-                       ecore_x_icccm_name_class_set(pcd->lock_win, "comp_data->lock_win", "comp_data->lock_win");
-                    }
-               }
+               _e_comp_x_client_modal_setup(ec);
 
              if (e_config->focus_setting == E_FOCUS_NEW_DIALOG ||
                  (ec->parent->focused && (e_config->focus_setting == E_FOCUS_NEW_DIALOG_IF_OWNER_FOCUSED)))
index 33360e5..af1a01c 100644 (file)
@@ -36,6 +36,8 @@ struct _E_Comp_X_Client_Data
    Ecore_Timer *first_draw_delay; //configurable placebo
    Eina_Bool first_damage : 1; //ignore first damage on non-re_manage clients
 
+   unsigned int parent_activate_count; //number of times a win has activated itself when parent was focused
+
    struct
    {
       struct