diff options
| author | Po Lu | 2022-07-02 16:41:45 +0800 |
|---|---|---|
| committer | Po Lu | 2022-07-02 16:42:06 +0800 |
| commit | 50b3e9d23dbdcd8809aaa8a95f62c2df33868d25 (patch) | |
| tree | 335ec95f25895349401232afb564c1c9bba6594c /src | |
| parent | 9230953f23c432699347bb3eeadebd82e4cbbfaa (diff) | |
| download | emacs-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.c | 179 |
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. */ | ||
| 1843 | static bool | ||
| 1844 | x_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 | |||
| 1841 | static Window | 1878 | static Window |
| 1842 | xm_get_drag_window_1 (struct x_display_info *dpyinfo) | 1879 | xm_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; |