1 /* -copyright-
2 #-#
3 #-# xsnow: let it snow on your desktop
4 #-# Copyright (C) 1984,1988,1990,1993-1995,2000-2001 Rick Jansen
5 #-# 	      2019,2020,2021 Willem Vermin
6 #-#
7 #-# This program is free software: you can redistribute it and/or modify
8 #-# it under the terms of the GNU General Public License as published by
9 #-# the Free Software Foundation, either version 3 of the License, or
10 #-# (at your option) any later version.
11 #-#
12 #-# This program is distributed in the hope that it will be useful,
13 #-# but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #-# GNU General Public License for more details.
16 #-#
17 #-# You should have received a copy of the GNU General Public License
18 #-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #-#
20  */
21 #include <gtk/gtk.h>
22 #include <gdk/gdkx.h>
23 #include <X11/Intrinsic.h>
24 #include "transwindow.h"
25 #include "debug.h"
26 
27 
28 static int setvaria(GtkWidget *widget);
29 
30 /*
31  * creates transparent window using gtk3/cairo.
32  * transwindow: (input)  GtkWidget to create transparent window in
33  * fullscreen:  (input)  full-screen or not full screen
34  * sticky:      (input)  visible on all workspaces or not
35  * below:       (input)  1: below all other windows 2: above all other windows 0: no action
36  * dock:        (input)  make it a 'dock' window: no decoration and not interfering with xsnow, xpenguins
37  *                       NOTE: with dock=1, gtk ignores the value of below: window is above all other windows
38  *                       NOTE: with decorations set to TRUE (see gtk_window_set_decorated()),
39  *                             the window is not click-through in Gnome
40  *                             So: dock = 1 is good for Gnome, or call gtk_window_set_decorated(w,FALSE)
41  *                             before this function
42  * gdk_window:  (output) GdkWindow created
43  * x11_window:  (output) Window X11 window created
44  */
make_trans_window(GtkWidget * transwindow,int fullscreen,int sticky,int below,int dock,GdkWindow ** gdk_window,Window * x11_window)45 int make_trans_window(GtkWidget *transwindow, int fullscreen, int sticky, int below, int dock,
46       GdkWindow **gdk_window, Window *x11_window)
47 {
48    if(gdk_window)
49       *gdk_window = NULL;
50    if(x11_window)
51       *x11_window = 0;
52 
53    // We take full responsibility for drawing background etc.
54    // Also, this is essential to obtain the desired effect.
55    gtk_widget_set_app_paintable(transwindow, TRUE);
56 
57    // essential in Gnome:
58    gtk_window_set_decorated(GTK_WINDOW(transwindow),FALSE);
59    // essential everywhere:
60    gtk_window_set_accept_focus(GTK_WINDOW(transwindow), FALSE);
61 
62    // take care that 'below' and 'sticky' are taken care of in gtk_main loop:
63    g_signal_connect(transwindow, "draw", G_CALLBACK(setvaria), NULL);
64 
65    // remove our things from transwindow:
66    g_object_steal_data(G_OBJECT(transwindow),"trans_sticky");
67    g_object_steal_data(G_OBJECT(transwindow),"trans_below");
68    g_object_steal_data(G_OBJECT(transwindow),"trans_nobelow");
69    g_object_steal_data(G_OBJECT(transwindow),"trans_done");
70 
71 
72    static char somechar;
73    if (sticky)
74       g_object_set_data(G_OBJECT(transwindow),"trans_sticky",&somechar);
75 
76    switch(below)
77    {
78       case 0:
79 	 g_object_set_data(G_OBJECT(transwindow),"trans_nobelow",&somechar);
80 	 break;
81       case 1:
82 	 g_object_set_data(G_OBJECT(transwindow),"trans_below",&somechar);
83 	 break;
84    }
85 
86    /* To check if the display supports alpha channels, get the visual */
87    GdkScreen *screen = gtk_widget_get_screen(transwindow);
88 
89    if (!gdk_screen_is_composited(screen))
90    {
91       P("No alpha\n");
92       gtk_window_close(GTK_WINDOW(transwindow));
93       return FALSE;
94    }
95 
96    // Ensure the widget (the window, actually) can take RGBA
97    gtk_widget_set_visual(transwindow, gdk_screen_get_rgba_visual(screen));
98 
99    // set full screen if so desired:
100    if(fullscreen)
101    {
102       P("fullscreen\n");
103       XWindowAttributes attr;
104       Display *display = XOpenDisplay(NULL);
105       XGetWindowAttributes(display,DefaultRootWindow(display),&attr);
106       gtk_widget_set_size_request(GTK_WIDGET(transwindow),attr.width,attr.height);
107       XCloseDisplay(display);
108    }
109    else
110    {
111       P("NOT fullsscreen\n");
112    }
113 
114    gtk_widget_show_all(transwindow);
115    GdkWindow *gdkwin = gtk_widget_get_window(GTK_WIDGET(transwindow));
116 
117    // so that apps like xsnow will ignore this window:
118    if(dock)
119       gdk_window_set_type_hint(gdkwin,GDK_WINDOW_TYPE_HINT_DOCK);
120 
121    gdk_window_hide(gdkwin);
122    if(fullscreen)
123       gdk_window_move(gdkwin,0,0);
124    gdk_window_show(gdkwin);
125 
126    if (x11_window)
127       *x11_window = gdk_x11_window_get_xid(gdkwin);
128    if (gdk_window)
129       *gdk_window = gdkwin;
130 
131    usleep(200000);  // seems sometimes to be necessary with nvidia
132 
133    // just to be sure all settings are communicated with the server
134    gtk_widget_hide(transwindow);
135    gtk_widget_show_all(transwindow);
136 
137    // set some things, but note that this has to be repeated in the gkt_main loop.
138 
139    P("explicitly call setvaria\n");
140    setvaria(transwindow);
141    P("end explicit call\n");
142    g_object_steal_data(G_OBJECT(transwindow),"trans_done");
143 
144    return TRUE;
145 }
146 
147 // for some reason, in some environments the 'below' and 'stick' properties
148 // disappear. It works again, if we express our wishes after starting gtk_main
149 // and the best place is in the draw event.
150 //
setvaria(GtkWidget * widget)151 int setvaria(GtkWidget *widget)
152 {
153    // We want to reset the settings at least once to be sure.
154    // Things like sticky and below should be stored in the widget beforehand.
155    // Use the value of p itself, not what it points to.
156    // Following the C standard, we have to use an array to subtract pointers.
157    enum {rep = 1,nrep}; // must be >= 0, and is equal to the number of times the settings
158    //                      will be done when called more than once
159    static char something[nrep];
160    char *p = (char *)g_object_get_data(G_OBJECT(widget),"trans_done");
161    if (!p)
162       p = &something[0];
163    P("setvaria %p %p %d\n",p,&something,(int)(p-&something[0]));
164    if (p - &something[0] >=  rep)
165       return FALSE;
166    p++;
167    g_object_set_data(G_OBJECT(widget),"trans_done",p);
168 
169    P("setvaria %p %p %d\n",p, (void *)widget,(int)(p - &something[0]));
170 
171 
172    GdkWindow *gdk_window1 = gtk_widget_get_window(widget);
173    const int Usepassthru = 0;
174    if(Usepassthru)
175       gdk_window_set_pass_through(gdk_window1,TRUE); // does not work as expected
176    else
177    {
178       cairo_region_t *cairo_region1 = cairo_region_create();
179       gdk_window_input_shape_combine_region(gdk_window1, cairo_region1, 0,0);
180       cairo_region_destroy(cairo_region1);
181    }
182    P("setvaria %d widget: %p gdkwin: %p passthru: %d\n",counter++,(void *)widget,(void *)gdk_window1,gdk_window_get_pass_through(gdk_window1));
183 
184 
185    if(!g_object_get_data(G_OBJECT(widget),"trans_nobelow"))
186    {
187       if(g_object_get_data(G_OBJECT(widget),"trans_below"))
188 	 setbelow(GTK_WINDOW(widget));
189       else
190 	 setabove(GTK_WINDOW(widget));
191    }
192 
193    if(1)
194    {
195       if(g_object_get_data(G_OBJECT(widget),"trans_sticky"))
196 	 gtk_window_stick(GTK_WINDOW(widget));
197       else
198 	 gtk_window_unstick(GTK_WINDOW(widget));
199    }
200 
201    return FALSE;
202 }
203 
204 
205 // Force window below or above other windows.
206 // It appears that, to get a window below other windows, it can be necessary
207 // to do first the opposite, and then vice-versa.
208 // These codes are probably somewhat too exuberant ....
setbelow(GtkWindow * w)209 void setbelow(GtkWindow *w)
210 {
211    gtk_window_set_keep_above(GTK_WINDOW(w), TRUE);
212    gtk_window_set_keep_below(GTK_WINDOW(w), TRUE);
213    GdkWindow *gdkw = gtk_widget_get_window(GTK_WIDGET(w));
214    Window xwin = gdk_x11_window_get_xid(gdkw);
215    XWindowChanges changes;
216    changes.stack_mode = Below;
217    Display *display = XOpenDisplay(NULL);
218    XConfigureWindow(display,xwin,CWStackMode,&changes);
219    P("setbelow %#lx\n",xwin);
220    XCloseDisplay(display);
221 }
222 
setabove(GtkWindow * w)223 void setabove(GtkWindow *w)
224 {
225    gtk_window_set_keep_below(GTK_WINDOW(w), TRUE);
226    gtk_window_set_keep_above(GTK_WINDOW(w), TRUE);
227    GdkWindow *gdkw = gtk_widget_get_window(GTK_WIDGET(w));
228    Window xwin = gdk_x11_window_get_xid(gdkw);
229    XWindowChanges changes;
230    changes.stack_mode = Above;
231    Display *display = XOpenDisplay(NULL);
232    XConfigureWindow(display,xwin,CWStackMode,&changes);
233    P("setabove %#lx\n",xwin);
234    XCloseDisplay(display);
235 }
236