]>
Commit | Line | Data |
---|---|---|
1 | /* -copyright- | |
2 | #-# Copyright © 2021 Eric Bina, Dave Black, TJ Phan, | |
3 | #-# Vincent Renardias, Willem Vermin | |
4 | #-# | |
5 | #-# Permission is hereby granted, free of charge, to any person | |
6 | #-# obtaining a copy of this software and associated documentation | |
7 | #-# files (the “Software”), to deal in the Software without | |
8 | #-# restriction, including without limitation the rights to use, | |
9 | #-# copy, modify, merge, publish, distribute, sublicense, and/or | |
10 | #-# sell copies of the Software, and to permit persons to whom | |
11 | #-# the Software is furnished to do so, subject to the following | |
12 | #-# conditions: | |
13 | #-# | |
14 | #-# The above copyright notice and this permission notice shall | |
15 | #-# be included in all copies or substantial portions of the Software. | |
16 | #-# | |
17 | #-# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, | |
18 | #-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
19 | #-# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
20 | #-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
21 | #-# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
22 | #-# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
23 | #-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
24 | #-# OTHER DEALINGS IN THE SOFTWARE. | |
25 | #-# | |
26 | */ | |
27 | #include <gtk/gtk.h> | |
28 | #include <gdk/gdkx.h> | |
29 | #include <X11/Intrinsic.h> | |
30 | #include "transwindow.h" | |
31 | #include "debug.h" | |
32 | ||
33 | ||
34 | static int setvaria(GtkWidget *widget); | |
35 | ||
36 | /* | |
37 | * creates transparent window using gtk3/cairo. | |
38 | * transwindow: (input) GtkWidget to create transparent window in | |
39 | * fullscreen: (input) full-screen or not full screen | |
40 | * sticky: (input) visible on all workspaces or not | |
41 | * below: (input) 1: below all other windows 2: above all other windows 0: no action | |
42 | * dock: (input) make it a 'dock' window: no decoration and not interfering with xsnow, xpenguins | |
43 | * NOTE: with dock=1, gtk ignores the value of below: window is above all other windows | |
44 | * NOTE: with decorations set to TRUE (see gtk_window_set_decorated()), | |
45 | * the window is not click-through in Gnome | |
46 | * So: dock = 1 is good for Gnome, or call gtk_window_set_decorated(w,FALSE) | |
47 | * before this function | |
48 | * gdk_window: (output) GdkWindow created | |
49 | * x11_window: (output) Window X11 window created | |
50 | */ | |
51 | int make_trans_window(GtkWidget *transwindow, int fullscreen, int sticky, int below, int dock, | |
52 | GdkWindow **gdk_window, Window *x11_window) | |
53 | { | |
54 | if(gdk_window) | |
55 | *gdk_window = NULL; | |
56 | if(x11_window) | |
57 | *x11_window = 0; | |
58 | ||
59 | // We take full responsibility for drawing background etc. | |
60 | // Also, this is essential to obtain the desired effect. | |
61 | gtk_widget_set_app_paintable(transwindow, TRUE); | |
62 | ||
63 | // essential in Gnome: | |
64 | gtk_window_set_decorated(GTK_WINDOW(transwindow),FALSE); | |
65 | // essential everywhere: | |
66 | gtk_window_set_accept_focus(GTK_WINDOW(transwindow), FALSE); | |
67 | ||
68 | // take care that 'below' and 'sticky' are taken care of in gtk_main loop: | |
69 | g_signal_connect(transwindow, "draw", G_CALLBACK(setvaria), NULL); | |
70 | ||
71 | // remove our things from transwindow: | |
72 | g_object_steal_data(G_OBJECT(transwindow),"trans_sticky"); | |
73 | g_object_steal_data(G_OBJECT(transwindow),"trans_below"); | |
74 | g_object_steal_data(G_OBJECT(transwindow),"trans_nobelow"); | |
75 | g_object_steal_data(G_OBJECT(transwindow),"trans_done"); | |
76 | ||
77 | ||
78 | static char somechar; | |
79 | if (sticky) | |
80 | g_object_set_data(G_OBJECT(transwindow),"trans_sticky",&somechar); | |
81 | ||
82 | switch(below) | |
83 | { | |
84 | case 0: | |
85 | g_object_set_data(G_OBJECT(transwindow),"trans_nobelow",&somechar); | |
86 | break; | |
87 | case 1: | |
88 | g_object_set_data(G_OBJECT(transwindow),"trans_below",&somechar); | |
89 | break; | |
90 | } | |
91 | ||
92 | /* To check if the display supports alpha channels, get the visual */ | |
93 | GdkScreen *screen = gtk_widget_get_screen(transwindow); | |
94 | ||
95 | if (!gdk_screen_is_composited(screen)) | |
96 | { | |
97 | P("No alpha\n"); | |
98 | gtk_window_close(GTK_WINDOW(transwindow)); | |
99 | return FALSE; | |
100 | } | |
101 | ||
102 | // Ensure the widget (the window, actually) can take RGBA | |
103 | gtk_widget_set_visual(transwindow, gdk_screen_get_rgba_visual(screen)); | |
104 | ||
105 | // set full screen if so desired: | |
106 | if(fullscreen) | |
107 | { | |
108 | P("fullscreen\n"); | |
109 | XWindowAttributes attr; | |
110 | Display *display = XOpenDisplay(NULL); | |
111 | XGetWindowAttributes(display,DefaultRootWindow(display),&attr); | |
112 | gtk_widget_set_size_request(GTK_WIDGET(transwindow),attr.width,attr.height); | |
113 | XCloseDisplay(display); | |
114 | } | |
115 | else | |
116 | { | |
117 | P("NOT fullsscreen\n"); | |
118 | } | |
119 | ||
120 | gtk_widget_show_all(transwindow); | |
121 | GdkWindow *gdkwin = gtk_widget_get_window(GTK_WIDGET(transwindow)); | |
122 | ||
123 | // so that apps like xsnow will ignore this window: | |
124 | if(dock) | |
125 | gdk_window_set_type_hint(gdkwin,GDK_WINDOW_TYPE_HINT_DOCK); | |
126 | ||
127 | gdk_window_hide(gdkwin); | |
128 | if(fullscreen) | |
129 | gdk_window_move(gdkwin,0,0); | |
130 | gdk_window_show(gdkwin); | |
131 | ||
132 | if (x11_window) | |
133 | *x11_window = gdk_x11_window_get_xid(gdkwin); | |
134 | if (gdk_window) | |
135 | *gdk_window = gdkwin; | |
136 | ||
137 | usleep(200000); // seems sometimes to be necessary with nvidia | |
138 | ||
139 | // just to be sure all settings are communicated with the server | |
140 | gtk_widget_hide(transwindow); | |
141 | gtk_widget_show_all(transwindow); | |
142 | ||
143 | // set some things, but note that this has to be repeated in the gkt_main loop. | |
144 | ||
145 | P("explicitly call setvaria\n"); | |
146 | setvaria(transwindow); | |
147 | P("end explicit call\n"); | |
148 | g_object_steal_data(G_OBJECT(transwindow),"trans_done"); | |
149 | ||
150 | return TRUE; | |
151 | } | |
152 | ||
153 | // for some reason, in some environments the 'below' and 'stick' properties | |
154 | // disappear. It works again, if we express our wishes after starting gtk_main | |
155 | // and the best place is in the draw event. | |
156 | // | |
157 | int setvaria(GtkWidget *widget) | |
158 | { | |
159 | // We want to reset the settings at least once to be sure. | |
160 | // Things like sticky and below should be stored in the widget beforehand. | |
161 | // Use the value of p itself, not what it points to. | |
162 | // Following the C standard, we have to use an array to subtract pointers. | |
163 | enum {rep = 1,nrep}; // must be >= 0, and is equal to the number of times the settings | |
164 | // will be done when called more than once | |
165 | static char something[nrep]; | |
166 | char *p = (char *)g_object_get_data(G_OBJECT(widget),"trans_done"); | |
167 | if (!p) | |
168 | p = &something[0]; | |
169 | P("setvaria %p %p %d\n",p,&something,(int)(p-&something[0])); | |
170 | if (p - &something[0] >= rep) | |
171 | return FALSE; | |
172 | p++; | |
173 | g_object_set_data(G_OBJECT(widget),"trans_done",p); | |
174 | ||
175 | P("setvaria %p %p %d\n",p, (void *)widget,(int)(p - &something[0])); | |
176 | ||
177 | ||
178 | GdkWindow *gdk_window1 = gtk_widget_get_window(widget); | |
179 | const int Usepassthru = 0; | |
180 | if(Usepassthru) | |
181 | gdk_window_set_pass_through(gdk_window1,TRUE); // does not work as expected | |
182 | else | |
183 | { | |
184 | cairo_region_t *cairo_region1 = cairo_region_create(); | |
185 | gdk_window_input_shape_combine_region(gdk_window1, cairo_region1, 0,0); | |
186 | cairo_region_destroy(cairo_region1); | |
187 | } | |
188 | P("setvaria %d widget: %p gdkwin: %p passthru: %d\n",counter++,(void *)widget,(void *)gdk_window1,gdk_window_get_pass_through(gdk_window1)); | |
189 | ||
190 | ||
191 | if(!g_object_get_data(G_OBJECT(widget),"trans_nobelow")) | |
192 | { | |
193 | if(g_object_get_data(G_OBJECT(widget),"trans_below")) | |
194 | setbelow(GTK_WINDOW(widget)); | |
195 | else | |
196 | setabove(GTK_WINDOW(widget)); | |
197 | } | |
198 | ||
199 | if(1) | |
200 | { | |
201 | if(g_object_get_data(G_OBJECT(widget),"trans_sticky")) | |
202 | gtk_window_stick(GTK_WINDOW(widget)); | |
203 | else | |
204 | gtk_window_unstick(GTK_WINDOW(widget)); | |
205 | } | |
206 | ||
207 | return FALSE; | |
208 | } | |
209 | ||
210 | ||
211 | // Force window below or above other windows. | |
212 | // It appears that, to get a window below other windows, it can be necessary | |
213 | // to do first the opposite, and then vice-versa. | |
214 | // These codes are probably somewhat too exuberant .... | |
215 | void setbelow(GtkWindow *w) | |
216 | { | |
217 | gtk_window_set_keep_above(GTK_WINDOW(w), TRUE); | |
218 | gtk_window_set_keep_below(GTK_WINDOW(w), TRUE); | |
219 | GdkWindow *gdkw = gtk_widget_get_window(GTK_WIDGET(w)); | |
220 | Window xwin = gdk_x11_window_get_xid(gdkw); | |
221 | XWindowChanges changes; | |
222 | changes.stack_mode = Below; | |
223 | Display *display = XOpenDisplay(NULL); | |
224 | XConfigureWindow(display,xwin,CWStackMode,&changes); | |
225 | P("setbelow %#lx\n",xwin); | |
226 | XCloseDisplay(display); | |
227 | } | |
228 | ||
229 | void setabove(GtkWindow *w) | |
230 | { | |
231 | gtk_window_set_keep_below(GTK_WINDOW(w), TRUE); | |
232 | gtk_window_set_keep_above(GTK_WINDOW(w), TRUE); | |
233 | GdkWindow *gdkw = gtk_widget_get_window(GTK_WIDGET(w)); | |
234 | Window xwin = gdk_x11_window_get_xid(gdkw); | |
235 | XWindowChanges changes; | |
236 | changes.stack_mode = Above; | |
237 | Display *display = XOpenDisplay(NULL); | |
238 | XConfigureWindow(display,xwin,CWStackMode,&changes); | |
239 | P("setabove %#lx\n",xwin); | |
240 | XCloseDisplay(display); | |
241 | } |