Skip to content

Instantly share code, notes, and snippets.

@phillipberndt
Last active April 26, 2024 23:59
Show Gist options
  • Save phillipberndt/7688785 to your computer and use it in GitHub Desktop.
Save phillipberndt/7688785 to your computer and use it in GitHub Desktop.
Fake Xinerama configuration to split the DELL Latitude E7440 two-external monitors into two virtual heads
WHAT THIS IS
------------
The DELL LATITUDE E7440's docking station supports two external displays, and
the notebook's Haswell architecture supports having three displays active at
the same time. It works well, but the two external monitors are merged into one
big display:
$ xrandr
Screen 0: minimum 320 x 200, current 5280 x 1080, maximum 32767 x 32767
eDP1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 309mm x 174mm
1920x1080 60.0*+ 59.9
[...]
DP1 disconnected (normal left inverted right x axis y axis)
HDMI1 disconnected (normal left inverted right x axis y axis)
DP2 connected 3360x1050+1920+0 (normal left inverted right x axis y axis) 474mm x 296mm
1680x1050 59.9 +
3360x1050 60.0*
[..]
HDMI2 disconnected (normal left inverted right x axis y axis)
VIRTUAL1 disconnected (normal left inverted right x axis y axis)
(Note: For some reason, 1680x1050 is favored over the wide resolution. But as
long as it is active, one does not see anything on the screens! Set 3360x1060
manually to see output)
I hacked this fake xinerama library to split the large output into two fake
xinerama outputs, such that maximizing works as expected in my window manager.
ACKNOWLEDGEMENTS
----------------
The fakexinerama.c file was taken from
http://lists.suckless.org/dev/0910/1599.html and was written by Kris Maglione
<maglione.k_AT_gmail.com>. I merely replaced some lines in it such that the
library would only be active if the large external monitor was actually
connected.
HOW-TO
------
1) Adjust XineramaQueryScreens() to your needs. Starting with line 57, I manually
change the configuration. Replace that with your configuration! x_org and y_org are
x and y coordinates of the screen's origin (top left corner) in the wide desktop.
2) Compile with `cc -fPIC -O2 -shared -o libXinerama.so fakexinerama.c -lX11' and create
a link libXinerama.so.1 to that library.
3) Move both files to /usr/local/lib
4) Restart, enjoy your working desktop :-)
If you want to test your configuration, test.c must be compiled with `cc -otest test.c -lXinerama -lX11'.
FAQ
---
Q: My two screens are mirrored. Does this library help?
A: No. Mirrored output is a setting, which can be changed using the xrandr tool. Have a look at
$ man xrandr
You likely search for
$ xrandr --output eDP1 --auto --output DP1 --right-of eDP1 --mode <some resolution>
Run xrandr without arguments first to find the names of the outputs and the mode (resolution). On the
two external screens, search for a resolution which seems unusually wide (in my above example,
3360x1050)
This library, and fakexrandr (https://github.com/phillipberndt/fakexrandr) serve another purpose: If
the two external screens appear as one wide screen in the output of xrandr, fullscreen, window
placement, etc. won't work correctly (programs maximize to both screens or are centered in between).
Window managers use Xinerama and XRandR to query the screen information. The fake* libraries hook
into the real libraries and replace information on the one large screen with two smaller ones,
solving the maximization problem.
#ifdef notdef
set -x
exec cc -O2 -std=c99 -pedantic -Wall $0 -fPIC -o libXinerama.so -shared
#endif
#define _XOPEN_SOURCE 600
#include <X11/Xlibint.h>
#include <X11/extensions/Xinerama.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>
typedef XineramaScreenInfo* (queryfn)(Display*, int*);
typedef Status (versionfn)(Display*, int*, int*);
typedef Bool (extensionfn)(Display*, int*, int*);
typedef Bool (activefn)(Display*);
static void* libxinerama;
static queryfn* queryscreens;
static extensionfn* queryextension;
static versionfn* queryversion;
static activefn* isactive;
static void init() {
libxinerama = dlopen("/usr/lib/x86_64-linux-gnu/libXinerama.so.1", RTLD_GLOBAL | RTLD_LAZY);
if(!libxinerama)
abort();
isactive = (activefn*) (uintptr_t)dlsym(libxinerama, "XineramaIsActive");
queryversion = (versionfn*) (uintptr_t)dlsym(libxinerama, "XineramaQueryVersion");
queryextension = (extensionfn*) (uintptr_t)dlsym(libxinerama, "XineramaQueryExtension");
queryscreens = (queryfn*) (uintptr_t)dlsym(libxinerama, "XineramaQueryScreens");
}
Bool XineramaQueryExtension(Display *dpy, int *event_base, int *error_base) {
init();
return queryextension(dpy, event_base, error_base);
}
Status XineramaQueryVersion(Display *dpy, int *major, int *minor) {
init();
return queryversion(dpy, major, minor);
}
Bool XineramaIsActive(Display *dpy) {
init();
return isactive(dpy);
}
XineramaScreenInfo* XineramaQueryScreens(Display *dpy, int *number)
{
XineramaScreenInfo *ret, *work;
init();
ret = queryscreens(dpy, number);
if(*number == 2 && ret[1].width == 3360) {
work = Xmalloc(3 * sizeof(XineramaScreenInfo));
memcpy(work, ret, sizeof(XineramaScreenInfo));
work[1].screen_number = 1;
work[1].x_org = 1920;
work[1].y_org = 0;
work[1].width = 3360/2;
work[1].height = 1050;
work[2].screen_number = 2;
work[2].x_org = 1920 + 3360/2;
work[2].y_org = 0;
work[2].width = 3360/2;
work[2].height = 1050;
*number = 3;
XFree(ret);
ret = work;
}
return ret;
}
#define _XOPEN_SOURCE 600
#include <X11/Xlibint.h>
#include <X11/extensions/Xinerama.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>
void main() {
Display *dpl = XOpenDisplay(":0");
if(XineramaIsActive(dpl)) {
printf("Xinerama is active\n");
int screens;
XineramaScreenInfo* s = XineramaQueryScreens(dpl, &screens);
printf("%d screens\n", screens);
int j;
for(j=0; j<screens; j++) {
printf("%d: %hd,%hd %hdx%hd\n", j, s[j].x_org, s[j].y_org, s[j].width, s[j].height);
}
XFree(s);
}
else {
printf("Xinerama is not active\n");
}
XCloseDisplay(dpl);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment