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