New upstream version 3.1.1
[xfishtank.git] / src / transwindow.c
diff --git a/src/transwindow.c b/src/transwindow.c
new file mode 100644 (file)
index 0000000..9c5e309
--- /dev/null
@@ -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 <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <X11/Intrinsic.h>
+#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);
+}
This page took 0.012344 seconds and 4 git commands to generate.