Skip to content

Instantly share code, notes, and snippets.

@mmozeiko
Last active April 28, 2024 20:52
Show Gist options
  • Save mmozeiko/2401933b1fa89e5d5bd238b33eab0465 to your computer and use it in GitHub Desktop.
Save mmozeiko/2401933b1fa89e5d5bd238b33eab0465 to your computer and use it in GitHub Desktop.
gtk widget from x11 window
// gcc test.c `pkg-config --cflags --libs gtk+-3.0 gdk-3.0` -lX11 && ./a.out
#include <X11/Xlib.h>
#include <unistd.h>
#include <stdio.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
static void my_gtk_realize(GtkWidget* widget, gpointer user)
{
gtk_widget_set_window(widget, (GdkWindow*)user);
}
static void file_quit(GtkWidget* widget, gpointer user)
{
gboolean* running = (gboolean*)user;
*running = FALSE;
}
int main(int argc, char** argv)
{
gtk_init(&argc, &argv);
GdkDisplay* gd = gdk_display_get_default();
Display* d = GDK_DISPLAY_XDISPLAY(gd);
Window w = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, 300, 300, 0, 0, 0);
XMapRaised(d, w);
GdkWindow* gw = gdk_x11_window_foreign_new_for_display(gd, w);
GtkWidget* gtk = gtk_widget_new(GTK_TYPE_WINDOW, NULL);
g_signal_connect(gtk, "realize", G_CALLBACK(my_gtk_realize), gw);
gtk_widget_set_has_window(gtk, TRUE);
gtk_widget_realize(gtk);
GtkWidget* menubar = gtk_menu_bar_new();
GtkWidget* file = gtk_menu_item_new_with_label("File");
GtkWidget* filemenu = gtk_menu_new();
GtkWidget* quit = gtk_menu_item_new_with_label("Quit");
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
GtkWidget* box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_box_pack_start(GTK_BOX(box), menubar, FALSE, FALSE, 3);
gtk_container_add(GTK_CONTAINER(gtk), box);
gboolean running = TRUE;
g_signal_connect(G_OBJECT(quit), "activate", G_CALLBACK(file_quit), &running);
gtk_widget_show_all(gtk);
GC gc = XCreateGC(d, w, 0, 0);
XColor red;
XAllocNamedColor(d, DefaultColormap(d, DefaultScreen(d)), "red", &red, &red);
XSetForeground(d, gc, red.pixel);
while (running)
{
while (gtk_events_pending())
{
gtk_main_iteration();
}
XDrawRectangle(d, w, gc, 100, 100, 200, 200);
}
}
@Misko-2083
Copy link

Thanks for sharing.
I tried to make it work with popover. Like a popup menu that closes when it loses focus.
It's also undecorated and trasparent window. setting MOTIF_WM_HINTS is the only thing that work with Xfce.
Sometimes it gets buggy and keeps drawing black window behind the popover.

// gcc test.c `pkg-config --cflags --libs gtk+-3.0 gdk-3.0 glib-2.0` -lX11 && ./a.out

#include <X11/Xlib.h>
#include <unistd.h>
#include <stdio.h>

#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <glib.h>


static void my_gtk_realize(GtkWidget* widget, gpointer data)
{
    gtk_widget_set_window(widget, (GdkWindow*)data);
}

static void file_quit(GtkWidget* widget, gpointer data)
{
    gboolean* running = (gboolean*)data;
    *running = FALSE;
}

int main(int argc, char** argv)
{
    gtk_init(&argc, &argv);

    XVisualInfo vinfo;

    GdkDisplay* gd = gdk_display_get_default();
    Display* d = GDK_DISPLAY_XDISPLAY(gd);
    cairo_rectangle_int_t    rect;

    XMatchVisualInfo(d, DefaultScreen(d), 32, TrueColor, &vinfo);

    XSetWindowAttributes attr;

    attr.colormap = XCreateColormap(d, DefaultRootWindow(d), vinfo.visual, AllocNone);
    attr.border_pixel = 0;
    attr.background_pixel = 0;

    Screen *scr = DefaultScreenOfDisplay(d);

    Window w = XCreateWindow(d, RootWindowOfScreen(scr), 0, 0, 300, 300, 0, vinfo.depth,
                             InputOutput, vinfo.visual, CWColormap | CWBorderPixel | CWBackPixel, &attr);

    XSelectInput(d, w, StructureNotifyMask);
    GC gc = XCreateGC(d, w, 0, 0);

    Atom wm_delete_window = XInternAtom(d, "WM_DELETE_WINDOW", 0);
    XSetWMProtocols(d, w, &wm_delete_window, 1);

    Atom WM_HINTS;
    int set;

    WM_HINTS = XInternAtom(d, "_MOTIF_WM_HINTS", True);

    if ( WM_HINTS != None ) {
        #define MWM_DECOR_NONE          0
        #define MWM_HINTS_DECORATIONS   (1L << 1)
        struct {
          unsigned long flags;
          unsigned long functions;
          unsigned long decorations;
                   long input_mode;
          unsigned long status;
        } MWMHints = { MWM_HINTS_DECORATIONS, 0,
            MWM_DECOR_NONE, 0, 0 };
        XChangeProperty(d, w, WM_HINTS, WM_HINTS, 32,
                        PropModeReplace, (unsigned char *)&MWMHints,
                        sizeof(MWMHints)/4);
    }

    XMapRaised(d, w);

    GdkWindow* gw = gdk_x11_window_foreign_new_for_display(gd, w);

    GtkWidget* gtk = gtk_widget_new(GTK_TYPE_WINDOW, NULL);

    GtkWidget        *close_button;
    GtkWidget        *box;

    g_signal_connect(gtk, "realize", G_CALLBACK(my_gtk_realize), gw);
    gtk_widget_set_has_window(gtk, TRUE);
    gtk_widget_realize(gtk);

    GtkWidget* popover = gtk_popover_new(gtk);

    gtk_popover_set_constrain_to(GTK_POPOVER(popover),
                                 GTK_POPOVER_CONSTRAINT_NONE);
    rect.x = 5;
    rect.y = 500;
    rect.height = 35;
    rect.width = 35;

    gtk_popover_set_pointing_to(GTK_POPOVER(popover), &rect);
    gtk_popover_set_position(GTK_POPOVER(popover), GTK_POS_TOP);

    gtk_popover_set_modal(GTK_POPOVER(popover), TRUE);

    box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
    close_button = gtk_button_new_with_label("Close");
    gtk_widget_set_sensitive (close_button, TRUE);

    gtk_box_pack_start (GTK_BOX(box), close_button, TRUE, FALSE, 10);

    gtk_container_add(GTK_CONTAINER(popover), box);

    gtk_container_add(GTK_CONTAINER(gtk), popover);
    gtk_container_set_border_width (GTK_CONTAINER(gtk),
                                     20);
    gboolean running = TRUE;

    g_signal_connect(G_OBJECT(popover), "closed", G_CALLBACK(file_quit), &running);

    g_signal_connect(G_OBJECT(close_button), "clicked", G_CALLBACK(file_quit), &running);

    gtk_widget_show_all(gtk);

    while (running)
    {
        Window focus;
        int revert_to;
        XGetInputFocus(d, &focus, &revert_to); // see man
        if (focus != w)
           running = FALSE;

        while (g_main_context_pending(NULL))
        {
            g_main_context_iteration(NULL,FALSE);
        }
    }

    XDestroyWindow(d, w);
    XCloseDisplay(d);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment