Skip to content

Commit

Permalink
Get X background via Xlib, to remove flicker and obscuring issues
Browse files Browse the repository at this point in the history
Currently, this does not work for non-image backgrounds, like when a color is set via icewmbg. Will then produce a black border.
  • Loading branch information
onli committed Sep 17, 2024
1 parent d921343 commit c6f3440
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 141 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ SRCDIR ?= src/
SHELL ?= /bin/sh
CC = g++
CCFLAGS ?= -O2
DEPS = $(shell pkg-config --libs --cflags glib-2.0 gtk+-3.0 libwnck-3.0 xcb-ewmh librsvg-2.0)
DEPS = $(shell pkg-config --libs --cflags glib-2.0 gtk+-3.0 libwnck-3.0 xcb-ewmh librsvg-2.0 x11)
ifneq (, $(shell which wx-config))
DEPS += $(shell wx-config --cflags --libs)
else
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Currently, simdock has to be compiled manually. The main dependencies are wxWidg

Under Void Linux:

sudo xbps-install pkg-config wxWidgets-gtk3-devel librsvg-devel xcb-ewmh libwnck-devel make gcc
sudo xbps-install pkg-config wxWidgets-gtk3-devel librsvg-devel xcb-ewmh libwnck-devel libX11-devel make gcc

1. Type `make` to compile
1. Type `sudo make install` to install
Expand Down
126 changes: 74 additions & 52 deletions src/background.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,64 +19,86 @@

using namespace std;

// Function to fetch the root window background pixmap
Pixmap getRootPixmap(Display* display, Window root) {
Atom actual_type;
int actual_format;
unsigned long nitems, bytes_after;
unsigned char* prop = nullptr;
Atom prop_root = XInternAtom(display, "_XROOTPMAP_ID", True);

if (XGetWindowProperty(display, root, prop_root, 0, 1, False, XA_PIXMAP,
&actual_type, &actual_format, &nitems, &bytes_after,
&prop) == Success) {
if (nitems == 1) {
Pixmap pixmap = *(Pixmap*)prop;
XFree(prop);
return pixmap;
}
}
return None;
}

// Function to convert X11 Pixmap into wxImage
wxImage ConvertPixmapToWxImage(Display* display, Pixmap pixmap, int width, int height) {
XImage* ximage = XGetImage(display, pixmap, 0, 0, width, height, AllPlanes, ZPixmap);
if (!ximage) {
std::cerr << "Failed to get XImage from Pixmap!" << std::endl;
return wxImage(width, height);
}

wxImage image(width, height);
unsigned char* data = image.GetData();
if (!data) {
std::cerr << "Failed to allocate memory for wxImage data!" << std::endl;
XDestroyImage(ximage);
return wxImage(width, height);
}

// Convert pixel data
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
unsigned long pixel = XGetPixel(ximage, x, y);
unsigned char red = (pixel >> 16) & 0xFF;
unsigned char green = (pixel >> 8) & 0xFF;
unsigned char blue = pixel & 0xFF;

data[(y * width + x) * 3 + 0] = red;
data[(y * width + x) * 3 + 1] = green;
data[(y * width + x) * 3 + 2] = blue;
}
}

XDestroyImage(ximage);
return image;
}

//get the currently used background
wxBitmap* getRootWallpaper()
{
wxBitmap* backImage = new wxBitmap();
WnckScreen *screen = wnck_screen_get_default ();
wxBitmap* backImage;
Display* display = XOpenDisplay(nullptr);
if (!display) {
std::cerr << "Unable to open X display" << std::endl;
return NULL;
}

// This is the X window ID of the desktop
Pixmap pm = wnck_screen_get_background_pixmap(screen);
int i = 0;
while(i < 5)
{
if (pm != None) {
break;
Window root = DefaultRootWindow(display);

// Get the root window background pixmap
Pixmap rootPixmap = getRootPixmap(display, root);
if (rootPixmap != None) {
int screen_width = DisplayWidth(display, DefaultScreen(display));
int screen_height = DisplayHeight(display, DefaultScreen(display));

// Convert Pixmap to wxImage and then to wxBitmap
wxImage image = ConvertPixmapToWxImage(display, rootPixmap, screen_width, screen_height);
if (image.IsOk()) {
backImage = new wxBitmap(image);
}
wxMilliSleep(1000);
pm = wnck_screen_get_background_pixmap(screen);
i++;
}

if (pm != None) {
wxSize sz = wxGetDisplaySize();
// Note: This code to hide the app is duplicated with the outer call in tasks.cc, but this is needed for first app start
wxGetApp().frame->SetTransparent(0);
wxGetApp().frame->Hide();
wxGetApp().frame->Disable();
// Give the main UI thread a chance to hide the app first
while(wxGetApp().frame->IsShown()) {
wxMilliSleep(20);
}
// This sleep should be unnecessary, but without it the UI will not be hidden in the next step
wxMilliSleep(20);
wxBitmap* backImage = new wxBitmap(
gdk_pixbuf_get_from_window(
gdk_x11_window_foreign_new_for_display(
gdk_display_get_default(),
gdk_x11_get_default_root_xwindow()
),
0,
0,
sz.GetWidth(),
sz.GetHeight()
)
);
wxGetApp().frame->Show();
wxGetApp().frame->SetTransparent(255);
wxGetApp().frame->Enable();
return backImage;
} else {
wxSize sz = wxGetDisplaySize();
wxBitmap* backImage = new wxBitmap(sz.GetWidth(), sz.GetHeight());
wxMemoryDC dc;
dc.SelectObject(*backImage);
dc.SetBackground(*wxTRANSPARENT_BRUSH);
dc.Clear();
dc.SelectObject(*backImage);
return backImage;
}
return backImage;
XCloseDisplay(display);
return backImage;
}

wxBitmap *fixImage (wxString url, int type, wxColour c)
Expand Down
2 changes: 2 additions & 0 deletions src/background.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#define BACKGROUND_H_

#include "main.h"
#include <X11/Xlib.h>
#include <X11/Xatom.h>

wxBitmap *fixImage (wxString img, int type, wxColour c);
wxBitmap* getRootWallpaper();
Expand Down
10 changes: 0 additions & 10 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,6 @@ WX_DEFINE_ARRAY (simImage *, ImagesArray);
#define NONE_STR "none"
#define WALLPAPER_STR "wallpaper"

//#include <wx/arrimpl.cpp>
//WX_DEFINE_OBJARRAY(simImage)
//WX_DEFINE_OBJARRAY(ImagesList);


//WX_DECLARE_LIST(simImage, ImagesList);

// the ID we'll use to identify our event
//const int BACKGROUND_UPDATE_ID = 100000;


/* wxString to std::string converter. Useful for printing stuff */
std::string wx2std (const wxString & input);
Expand Down
10 changes: 0 additions & 10 deletions src/myFrame.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,6 @@ EVT_MENU (ID_Keep, MyFrame::OnKeep)
EVT_MENU (ID_Edit, MyFrame::OnEdit)
EVT_MENU (ID_Delete, MyFrame::OnDelete)
/*
* Custom
*/
//EVT_COMMAND (BACKGROUND_UPDATE_ID, wxEVT_COMMAND_TEXT_UPDATED, MyFrame::OnDelayedBackground)
/*
* Painting
*/
EVT_PAINT (MyFrame::OnPaint)
Expand Down Expand Up @@ -250,12 +246,6 @@ MyFrame::GetWallpaper ()
{
return backImage;
}

//void
//MyFrame::OnDelayedBackground(wxCommandEvent &event) {
//std::cout << "got update background event" << std::endl;
//update_background();
//}

void MyFrame::SetMarkBitmap (wxBitmap * newBmp)
{
Expand Down
1 change: 0 additions & 1 deletion src/myFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ class MyFrame:public wxFrame

SettingsDialog *settingsDialog;
void OnClose (wxCloseEvent &event);
//void OnDelayedBackground(wxCommandEvent &event);

DECLARE_EVENT_TABLE ()

Expand Down
67 changes: 2 additions & 65 deletions src/tasks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -207,78 +207,15 @@ bool check_overlap(int x1, int y1, int width1, int height1,
return false;
}

// Check whether our simdock window intersects with some different window
bool is_covered(WnckScreen *screen) {
GdkDisplay* display = gdk_display_get_default();
GtkWidget* widget = wxGetApp().frame->GetHandle();
GdkWindow* gdkWindow = gtk_widget_get_window(widget);
XID xid = gdk_x11_window_get_xid(gdkWindow);
WnckWindow* frameWindow = wnck_window_get(xid);

int win1_x, win1_y, win1_width, win1_height;
wnck_window_get_geometry(frameWindow, &win1_x, &win1_y, &win1_width, &win1_height);

GList* allWindows = wnck_screen_get_windows(screen);
for (GList *l = allWindows; l != NULL; l = l->next) {
WnckWindow* curWindow = WNCK_WINDOW(l->data);
if (wnck_window_is_visible_on_workspace(curWindow, wnck_screen_get_active_workspace(screen))) {
Window otherXid = wnck_window_get_xid(curWindow);

// We use the XID here just to avoid confusion with different pointers
if (xid != otherXid) {
int win2_x, win2_y, win2_width, win2_height;
wnck_window_get_geometry(curWindow, &win2_x, &win2_y, &win2_width, &win2_height);
if (check_overlap(win1_x, win1_y, win1_width, win1_height, win2_x, win2_y, win2_width, win2_height)) {
return true;
}
}
}
}
return false;
}

// Change the background for pseudo transparency mode
void update_background() {
std::cout << "updating background" << std::endl;
// The signal arrives before the background is actually changed. The small sleep workarounds this issue.
wxMilliSleep(20);
// Now we hide the app, because getRootWallpaper() since the switch to GTK3 also sees simdock itself
wxGetApp().frame->SetTransparent(0);
wxGetApp().frame->Hide();
wxGetApp().frame->Disable();
wxGetApp().CallAfter([]{
// The use of CallAfter is needed, otherwise the app won't be hidden while getRoootWallpaper runs
wxBitmap* backImage = getRootWallpaper();
wxGetApp().SetWallpaper(backImage);
});
}

void tasks_window_background_change (WnckScreen *screen, WnckWindow *window, callbackArgs* ca) {
if (wxGetApp().frame->IsTransparentBackgroundSupported()) {
// With real transparency enabled we have to do nothing here
return;
}

if (is_covered(screen)) {
// Without real transparency, we don't want to continue if the frame is covered. It would
// make us fetch the overlapping window as part of the background.
std::thread([]() {
WnckScreen* screen = wnck_screen_get_default();
do {
std::cout << "sleeping" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5)); // Delay of 5 seconds
} while (is_covered(screen));
std::cout << "send event" << std::endl;
//wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, BACKGROUND_UPDATE_ID);
//wxGetApp().frame->GetEventHandler()->AddPendingEvent(event);
wxGetApp().CallAfter([]{
update_background();
});
}).detach();
return;
}

update_background();
wxBitmap* backImage = getRootWallpaper();
wxGetApp().SetWallpaper(backImage);
}

void tasks_track_active_window (WnckScreen *screen, WnckWindow *window, callbackArgs* ca) {
Expand Down
1 change: 0 additions & 1 deletion src/tasks.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,5 @@ void tasks_register_signals(ImagesArray * ImagesList,simSettings settings);
/* Adds a new image to the images list given a window and task informations */
void tasks_addNewImage(WnckWindow *window, ImagesArray* ImagesList, simSettings settings,const taskInfo& ti);
wxBitmap* tasks_getRootWallpaper();
void update_background();

#endif

0 comments on commit c6f3440

Please sign in to comment.