X-Git-Url: http://git.ieval.ro/?p=xfishtank.git;a=blobdiff_plain;f=src%2Ftranswindow.c;fp=src%2Ftranswindow.c;h=9c5e309e4e9ad8a5bc9a29bf6d363f52bee57a2e;hp=0000000000000000000000000000000000000000;hb=4c1bd65b5f48075ff80be1e401beb5ff21b4b89e;hpb=4deccbc95c1e3e86669d84c2f6922ee6a5f5180a diff --git a/src/transwindow.c b/src/transwindow.c new file mode 100644 index 0000000..9c5e309 --- /dev/null +++ b/src/transwindow.c @@ -0,0 +1,241 @@ +/* -copyright- +#-# Copyright © 2021 Eric Bina, Dave Black, TJ Phan, +#-# Vincent Renardias, Willem Vermin +#-# +#-# Permission is hereby granted, free of charge, to any person +#-# obtaining a copy of this software and associated documentation +#-# files (the “Software”), to deal in the Software without +#-# restriction, including without limitation the rights to use, +#-# copy, modify, merge, publish, distribute, sublicense, and/or +#-# sell copies of the Software, and to permit persons to whom +#-# the Software is furnished to do so, subject to the following +#-# conditions: +#-# +#-# The above copyright notice and this permission notice shall +#-# be included in all copies or substantial portions of the Software. +#-# +#-# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +#-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +#-# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +#-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#-# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +#-# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +#-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +#-# OTHER DEALINGS IN THE SOFTWARE. +#-# +*/ +#include +#include +#include +#include "transwindow.h" +#include "debug.h" + + +static int setvaria(GtkWidget *widget); + +/* + * creates transparent window using gtk3/cairo. + * transwindow: (input) GtkWidget to create transparent window in + * fullscreen: (input) full-screen or not full screen + * sticky: (input) visible on all workspaces or not + * below: (input) 1: below all other windows 2: above all other windows 0: no action + * dock: (input) make it a 'dock' window: no decoration and not interfering with xsnow, xpenguins + * NOTE: with dock=1, gtk ignores the value of below: window is above all other windows + * NOTE: with decorations set to TRUE (see gtk_window_set_decorated()), + * the window is not click-through in Gnome + * So: dock = 1 is good for Gnome, or call gtk_window_set_decorated(w,FALSE) + * before this function + * gdk_window: (output) GdkWindow created + * x11_window: (output) Window X11 window created + */ +int make_trans_window(GtkWidget *transwindow, int fullscreen, int sticky, int below, int dock, + GdkWindow **gdk_window, Window *x11_window) +{ + if(gdk_window) + *gdk_window = NULL; + if(x11_window) + *x11_window = 0; + + // We take full responsibility for drawing background etc. + // Also, this is essential to obtain the desired effect. + gtk_widget_set_app_paintable(transwindow, TRUE); + + // essential in Gnome: + gtk_window_set_decorated(GTK_WINDOW(transwindow),FALSE); + // essential everywhere: + gtk_window_set_accept_focus(GTK_WINDOW(transwindow), FALSE); + + // take care that 'below' and 'sticky' are taken care of in gtk_main loop: + g_signal_connect(transwindow, "draw", G_CALLBACK(setvaria), NULL); + + // remove our things from transwindow: + g_object_steal_data(G_OBJECT(transwindow),"trans_sticky"); + g_object_steal_data(G_OBJECT(transwindow),"trans_below"); + g_object_steal_data(G_OBJECT(transwindow),"trans_nobelow"); + g_object_steal_data(G_OBJECT(transwindow),"trans_done"); + + + static char somechar; + if (sticky) + g_object_set_data(G_OBJECT(transwindow),"trans_sticky",&somechar); + + switch(below) + { + case 0: + g_object_set_data(G_OBJECT(transwindow),"trans_nobelow",&somechar); + break; + case 1: + g_object_set_data(G_OBJECT(transwindow),"trans_below",&somechar); + break; + } + + /* To check if the display supports alpha channels, get the visual */ + GdkScreen *screen = gtk_widget_get_screen(transwindow); + + if (!gdk_screen_is_composited(screen)) + { + P("No alpha\n"); + gtk_window_close(GTK_WINDOW(transwindow)); + return FALSE; + } + + // Ensure the widget (the window, actually) can take RGBA + gtk_widget_set_visual(transwindow, gdk_screen_get_rgba_visual(screen)); + + // set full screen if so desired: + if(fullscreen) + { + P("fullscreen\n"); + XWindowAttributes attr; + Display *display = XOpenDisplay(NULL); + XGetWindowAttributes(display,DefaultRootWindow(display),&attr); + gtk_widget_set_size_request(GTK_WIDGET(transwindow),attr.width,attr.height); + XCloseDisplay(display); + } + else + { + P("NOT fullsscreen\n"); + } + + gtk_widget_show_all(transwindow); + GdkWindow *gdkwin = gtk_widget_get_window(GTK_WIDGET(transwindow)); + + // so that apps like xsnow will ignore this window: + if(dock) + gdk_window_set_type_hint(gdkwin,GDK_WINDOW_TYPE_HINT_DOCK); + + gdk_window_hide(gdkwin); + if(fullscreen) + gdk_window_move(gdkwin,0,0); + gdk_window_show(gdkwin); + + if (x11_window) + *x11_window = gdk_x11_window_get_xid(gdkwin); + if (gdk_window) + *gdk_window = gdkwin; + + usleep(200000); // seems sometimes to be necessary with nvidia + + // just to be sure all settings are communicated with the server + gtk_widget_hide(transwindow); + gtk_widget_show_all(transwindow); + + // set some things, but note that this has to be repeated in the gkt_main loop. + + P("explicitly call setvaria\n"); + setvaria(transwindow); + P("end explicit call\n"); + g_object_steal_data(G_OBJECT(transwindow),"trans_done"); + + return TRUE; +} + +// for some reason, in some environments the 'below' and 'stick' properties +// disappear. It works again, if we express our wishes after starting gtk_main +// and the best place is in the draw event. +// +int setvaria(GtkWidget *widget) +{ + // We want to reset the settings at least once to be sure. + // Things like sticky and below should be stored in the widget beforehand. + // Use the value of p itself, not what it points to. + // Following the C standard, we have to use an array to subtract pointers. + enum {rep = 1,nrep}; // must be >= 0, and is equal to the number of times the settings + // will be done when called more than once + static char something[nrep]; + char *p = (char *)g_object_get_data(G_OBJECT(widget),"trans_done"); + if (!p) + p = &something[0]; + P("setvaria %p %p %d\n",p,&something,(int)(p-&something[0])); + if (p - &something[0] >= rep) + return FALSE; + p++; + g_object_set_data(G_OBJECT(widget),"trans_done",p); + + P("setvaria %p %p %d\n",p, (void *)widget,(int)(p - &something[0])); + + + GdkWindow *gdk_window1 = gtk_widget_get_window(widget); + const int Usepassthru = 0; + if(Usepassthru) + gdk_window_set_pass_through(gdk_window1,TRUE); // does not work as expected + else + { + cairo_region_t *cairo_region1 = cairo_region_create(); + gdk_window_input_shape_combine_region(gdk_window1, cairo_region1, 0,0); + cairo_region_destroy(cairo_region1); + } + P("setvaria %d widget: %p gdkwin: %p passthru: %d\n",counter++,(void *)widget,(void *)gdk_window1,gdk_window_get_pass_through(gdk_window1)); + + + if(!g_object_get_data(G_OBJECT(widget),"trans_nobelow")) + { + if(g_object_get_data(G_OBJECT(widget),"trans_below")) + setbelow(GTK_WINDOW(widget)); + else + setabove(GTK_WINDOW(widget)); + } + + if(1) + { + if(g_object_get_data(G_OBJECT(widget),"trans_sticky")) + gtk_window_stick(GTK_WINDOW(widget)); + else + gtk_window_unstick(GTK_WINDOW(widget)); + } + + return FALSE; +} + + +// Force window below or above other windows. +// It appears that, to get a window below other windows, it can be necessary +// to do first the opposite, and then vice-versa. +// These codes are probably somewhat too exuberant .... +void setbelow(GtkWindow *w) +{ + gtk_window_set_keep_above(GTK_WINDOW(w), TRUE); + gtk_window_set_keep_below(GTK_WINDOW(w), TRUE); + GdkWindow *gdkw = gtk_widget_get_window(GTK_WIDGET(w)); + Window xwin = gdk_x11_window_get_xid(gdkw); + XWindowChanges changes; + changes.stack_mode = Below; + Display *display = XOpenDisplay(NULL); + XConfigureWindow(display,xwin,CWStackMode,&changes); + P("setbelow %#lx\n",xwin); + XCloseDisplay(display); +} + +void setabove(GtkWindow *w) +{ + gtk_window_set_keep_below(GTK_WINDOW(w), TRUE); + gtk_window_set_keep_above(GTK_WINDOW(w), TRUE); + GdkWindow *gdkw = gtk_widget_get_window(GTK_WIDGET(w)); + Window xwin = gdk_x11_window_get_xid(gdkw); + XWindowChanges changes; + changes.stack_mode = Above; + Display *display = XOpenDisplay(NULL); + XConfigureWindow(display,xwin,CWStackMode,&changes); + P("setabove %#lx\n",xwin); + XCloseDisplay(display); +}