aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPo Lu2022-07-02 16:41:45 +0800
committerPo Lu2022-07-02 16:42:06 +0800
commit50b3e9d23dbdcd8809aaa8a95f62c2df33868d25 (patch)
tree335ec95f25895349401232afb564c1c9bba6594c /src
parent9230953f23c432699347bb3eeadebd82e4cbbfaa (diff)
downloademacs-50b3e9d23dbdcd8809aaa8a95f62c2df33868d25.tar.gz
emacs-50b3e9d23dbdcd8809aaa8a95f62c2df33868d25.zip
Completely get rid of races during Motif drag window creation
* src/xterm.c (x_special_window_exists_p): New function. (xm_get_drag_window_1): Rework workflow and display grabbing.
Diffstat (limited to 'src')
-rw-r--r--src/xterm.c179
1 files changed, 96 insertions, 83 deletions
diff --git a/src/xterm.c b/src/xterm.c
index 245ffedb800..2629997f2ac 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -1838,10 +1838,47 @@ xm_drag_window_io_error_handler (Display *dpy)
1838 siglongjmp (x_dnd_disconnect_handler, 1); 1838 siglongjmp (x_dnd_disconnect_handler, 1);
1839} 1839}
1840 1840
1841/* Determine whether or not WINDOW exists on DPYINFO by selecting for
1842 input from it. */
1843static bool
1844x_special_window_exists_p (struct x_display_info *dpyinfo,
1845 Window window)
1846{
1847 bool rc;
1848
1849 x_catch_errors (dpyinfo->display);
1850 XSelectInput (dpyinfo->display, window,
1851 StructureNotifyMask);
1852 rc = !x_had_errors_p (dpyinfo->display);
1853 x_uncatch_errors_after_check ();
1854
1855 return rc;
1856}
1857
1858/* Drag window creation strategy (very tricky, but race-free):
1859
1860 First look for _MOTIF_DRAG_WINDOW. If it is already present,
1861 return it immediately to avoid the overhead of new display
1862 connections.
1863
1864 Otherwise, create a new connection to the display. In that
1865 connection, create a window, which will be the new drag window. Set
1866 the client disconnect mode of the new connection to
1867 RetainPermanent, and close it.
1868
1869 Grab the current display. Look up _MOTIF_DRAG_WINDOW, the current
1870 drag window. If it exists (which means _MOTIF_DRAG_WINDOW was
1871 created between the first step and now), kill the client that
1872 created the new drag window to free the client slot on the X
1873 server. Otherwise, set _MOTIF_DRAG_WINDOW to the new drag window.
1874
1875 Ungrab the display and return whichever window is currently in
1876 _MOTIF_DRAG_WINDOW. */
1877
1841static Window 1878static Window
1842xm_get_drag_window_1 (struct x_display_info *dpyinfo) 1879xm_get_drag_window_1 (struct x_display_info *dpyinfo)
1843{ 1880{
1844 Atom actual_type, _MOTIF_DRAG_WINDOW; 1881 Atom actual_type;
1845 int rc, actual_format; 1882 int rc, actual_format;
1846 unsigned long nitems, bytes_remaining; 1883 unsigned long nitems, bytes_remaining;
1847 unsigned char *tmp_data = NULL; 1884 unsigned char *tmp_data = NULL;
@@ -1851,9 +1888,9 @@ xm_get_drag_window_1 (struct x_display_info *dpyinfo)
1851 Emacs_XErrorHandler old_handler; 1888 Emacs_XErrorHandler old_handler;
1852 Emacs_XIOErrorHandler old_io_handler; 1889 Emacs_XIOErrorHandler old_io_handler;
1853 1890
1854 /* These are volatile because GCC mistakenly warns about them being 1891 /* This is volatile because GCC mistakenly warns about them being
1855 clobbered by longjmp. */ 1892 clobbered by longjmp. */
1856 volatile bool error, created; 1893 volatile bool error;
1857 1894
1858 drag_window = None; 1895 drag_window = None;
1859 rc = XGetWindowProperty (dpyinfo->display, dpyinfo->root_window, 1896 rc = XGetWindowProperty (dpyinfo->display, dpyinfo->root_window,
@@ -1862,26 +1899,20 @@ xm_get_drag_window_1 (struct x_display_info *dpyinfo)
1862 &actual_format, &nitems, &bytes_remaining, 1899 &actual_format, &nitems, &bytes_remaining,
1863 &tmp_data) == Success; 1900 &tmp_data) == Success;
1864 1901
1865 if (rc) 1902 if (rc && actual_type == XA_WINDOW
1903 && actual_format == 32 && nitems == 1
1904 && tmp_data)
1866 { 1905 {
1867 if (actual_type == XA_WINDOW 1906 drag_window = *(Window *) tmp_data;
1868 && actual_format == 32 && nitems == 1) 1907 rc = x_special_window_exists_p (dpyinfo, drag_window);
1869 {
1870 drag_window = *(Window *) tmp_data;
1871 x_catch_errors (dpyinfo->display);
1872 XSelectInput (dpyinfo->display, drag_window,
1873 StructureNotifyMask);
1874 rc = !x_had_errors_p (dpyinfo->display);
1875 x_uncatch_errors_after_check ();
1876 1908
1877 if (!rc) 1909 if (!rc)
1878 drag_window = None; 1910 drag_window = None;
1879 }
1880
1881 if (tmp_data)
1882 XFree (tmp_data);
1883 } 1911 }
1884 1912
1913 if (tmp_data)
1914 XFree (tmp_data);
1915
1885 if (drag_window == None) 1916 if (drag_window == None)
1886 { 1917 {
1887 block_input (); 1918 block_input ();
@@ -1910,74 +1941,22 @@ xm_get_drag_window_1 (struct x_display_info *dpyinfo)
1910 error = false; 1941 error = false;
1911 xm_drag_window_error = &error; 1942 xm_drag_window_error = &error;
1912 1943
1913 XGrabServer (temp_display);
1914 XSetCloseDownMode (temp_display, RetainPermanent); 1944 XSetCloseDownMode (temp_display, RetainPermanent);
1915
1916 old_handler = XSetErrorHandler (xm_drag_window_error_handler); 1945 old_handler = XSetErrorHandler (xm_drag_window_error_handler);
1917 1946
1918 _MOTIF_DRAG_WINDOW = XInternAtom (temp_display, 1947 attrs.override_redirect = True;
1919 "_MOTIF_DRAG_WINDOW", False); 1948 drag_window = XCreateWindow (temp_display, DefaultRootWindow (temp_display),
1920 1949 -1, -1, 1, 1, 0, CopyFromParent, InputOnly,
1921 if (error) 1950 CopyFromParent, CWOverrideRedirect, &attrs);
1922 goto give_up;
1923
1924 /* Some other program might've created a drag window between now
1925 and when we first looked. Use that if it exists. */
1926
1927 tmp_data = NULL;
1928 rc = XGetWindowProperty (temp_display, DefaultRootWindow (temp_display),
1929 _MOTIF_DRAG_WINDOW, 0, 1, False, XA_WINDOW,
1930 &actual_type, &actual_format, &nitems,
1931 &bytes_remaining, &tmp_data) == Success;
1932
1933 if (rc && actual_type == XA_WINDOW
1934 && actual_format == 32 && nitems == 1
1935 && tmp_data)
1936 drag_window = *(Window *) tmp_data;
1937
1938 if (tmp_data)
1939 XFree (tmp_data);
1940
1941 error = false;
1942
1943 if (drag_window == None)
1944 {
1945 created = true;
1946
1947 attrs.override_redirect = True;
1948 drag_window = XCreateWindow (temp_display, DefaultRootWindow (temp_display),
1949 -1, -1, 1, 1, 0, CopyFromParent, InputOnly,
1950 CopyFromParent, CWOverrideRedirect, &attrs);
1951 XChangeProperty (temp_display, DefaultRootWindow (temp_display),
1952 _MOTIF_DRAG_WINDOW, XA_WINDOW, 32, PropModeReplace,
1953 (unsigned char *) &drag_window, 1);
1954 }
1955 else
1956 created = false;
1957 1951
1958 /* Handle all errors now. */ 1952 /* Handle all errors now. */
1959 XSync (temp_display, False); 1953 XSync (temp_display, False);
1960 1954
1961 give_up:
1962
1963 /* Some part of the drag window creation process failed, so 1955 /* Some part of the drag window creation process failed, so
1964 punt. */ 1956 punt. Release all resources too. */
1965 if (error) 1957 if (error)
1966 { 1958 {
1967 XSetCloseDownMode (temp_display, DestroyAll); 1959 XSetCloseDownMode (temp_display, DestroyAll);
1968
1969 /* If the drag window was actually created, delete it now.
1970 Probably, a BadAlloc happened during the XChangeProperty
1971 request. */
1972 if (created)
1973 {
1974 if (drag_window != None)
1975 XDestroyWindow (temp_display, drag_window);
1976
1977 XDeleteProperty (temp_display, DefaultRootWindow (temp_display),
1978 _MOTIF_DRAG_WINDOW);
1979 }
1980
1981 drag_window = None; 1960 drag_window = None;
1982 } 1961 }
1983 1962
@@ -1995,15 +1974,49 @@ xm_get_drag_window_1 (struct x_display_info *dpyinfo)
1995 /* Make sure the drag window created is actually valid for the 1974 /* Make sure the drag window created is actually valid for the
1996 current display, and the XOpenDisplay above didn't 1975 current display, and the XOpenDisplay above didn't
1997 accidentally connect to some other display. */ 1976 accidentally connect to some other display. */
1998 x_catch_errors (dpyinfo->display); 1977 if (!x_special_window_exists_p (dpyinfo, drag_window))
1999 XSelectInput (dpyinfo->display, drag_window, StructureNotifyMask); 1978 drag_window = None;
2000 rc = !x_had_errors_p (dpyinfo->display);
2001 x_uncatch_errors_after_check ();
2002 unblock_input (); 1979 unblock_input ();
2003 1980
2004 /* We connected to the wrong display, so just give up. */ 1981 if (drag_window != None)
2005 if (!rc) 1982 {
2006 drag_window = None; 1983 XGrabServer (dpyinfo->display);
1984
1985 x_catch_errors (dpyinfo->display);
1986 tmp_data = NULL;
1987
1988 rc = XGetWindowProperty (dpyinfo->display, dpyinfo->root_window,
1989 dpyinfo->Xatom_MOTIF_DRAG_WINDOW,
1990 0, 1, False, XA_WINDOW, &actual_type,
1991 &actual_format, &nitems, &bytes_remaining,
1992 &tmp_data) == Success;
1993
1994 if (rc && actual_type == XA_WINDOW
1995 && actual_format == 32 && nitems == 1
1996 && tmp_data
1997 && x_special_window_exists_p (dpyinfo,
1998 *(Window *) tmp_data))
1999 {
2000 /* Kill the client now to avoid leaking a client slot,
2001 which is a limited resource. */
2002 XKillClient (dpyinfo->display, drag_window);
2003 drag_window = *(Window *) tmp_data;
2004 }
2005 else
2006 XChangeProperty (dpyinfo->display, dpyinfo->root_window,
2007 dpyinfo->Xatom_MOTIF_DRAG_WINDOW,
2008 XA_WINDOW, 32, PropModeReplace,
2009 (unsigned char *) &drag_window, 1);
2010
2011 if (tmp_data)
2012 XFree (tmp_data);
2013
2014 if (x_had_errors_p (dpyinfo->display))
2015 drag_window = None;
2016 x_uncatch_errors ();
2017
2018 XUngrabServer (dpyinfo->display);
2019 }
2007 } 2020 }
2008 2021
2009 return drag_window; 2022 return drag_window;