mirror of https://github.com/adamdruppe/arsd.git
xshm error handling improvement - now works with transparent fallback
This commit is contained in:
parent
1ccb81159d
commit
dcd4aa8b3b
112
simpledisplay.d
112
simpledisplay.d
|
@ -5806,6 +5806,11 @@ version(X11) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extern(C) int XShmErrorHandler (Display* dpy, XErrorEvent* evt) nothrow @nogc {
|
||||||
|
Image.impl.xshmfailed = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
private extern(C) int adrlogger (Display* dpy, XErrorEvent* evt) nothrow @nogc {
|
private extern(C) int adrlogger (Display* dpy, XErrorEvent* evt) nothrow @nogc {
|
||||||
import core.stdc.stdio;
|
import core.stdc.stdio;
|
||||||
char[265] buffer;
|
char[265] buffer;
|
||||||
|
@ -10988,9 +10993,10 @@ version(X11) {
|
||||||
auto display = XDisplayConnection.get;
|
auto display = XDisplayConnection.get;
|
||||||
auto font = XLoadQueryFont(display, xfontstr.ptr);
|
auto font = XLoadQueryFont(display, xfontstr.ptr);
|
||||||
// if the user font choice fails, fixed is pretty reliable (required by X to start!) and not bad either
|
// if the user font choice fails, fixed is pretty reliable (required by X to start!) and not bad either
|
||||||
xfontstr = "-*-fixed-medium-r-*-*-13-*-*-*-*-*-*-*";
|
if(font is null) {
|
||||||
if(font is null)
|
xfontstr = "-*-fixed-medium-r-*-*-13-*-*-*-*-*-*-*";
|
||||||
font = XLoadQueryFont(display, xfontstr.ptr);
|
font = XLoadQueryFont(display, xfontstr.ptr);
|
||||||
|
}
|
||||||
|
|
||||||
char** lol;
|
char** lol;
|
||||||
int lol2;
|
int lol2;
|
||||||
|
@ -12267,10 +12273,21 @@ mixin DynamicLoad!(XRender, "Xrender", 1, false, true) XRenderLibrary;
|
||||||
private __gshared char* displayName;
|
private __gshared char* displayName;
|
||||||
|
|
||||||
private __gshared int connectionSequence_;
|
private __gshared int connectionSequence_;
|
||||||
|
private __gshared bool isLocal_;
|
||||||
|
|
||||||
/// use this for lazy caching when reconnection
|
/// use this for lazy caching when reconnection
|
||||||
static int connectionSequenceNumber() { return connectionSequence_; }
|
static int connectionSequenceNumber() { return connectionSequence_; }
|
||||||
|
|
||||||
|
/++
|
||||||
|
Guesses if the connection appears to be local.
|
||||||
|
|
||||||
|
History:
|
||||||
|
Added June 3, 2021
|
||||||
|
+/
|
||||||
|
static @property bool isLocal() nothrow @trusted @nogc {
|
||||||
|
return isLocal_;
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts recreation of state, may require application assistance
|
/// Attempts recreation of state, may require application assistance
|
||||||
/// You MUST call this OUTSIDE the event loop. Let the exception kill the loop,
|
/// You MUST call this OUTSIDE the event loop. Let the exception kill the loop,
|
||||||
/// then call this, and if successful, reenter the loop.
|
/// then call this, and if successful, reenter the loop.
|
||||||
|
@ -12434,6 +12451,17 @@ mixin DynamicLoad!(XRender, "Xrender", 1, false, true) XRenderLibrary;
|
||||||
if(!librariesSuccessfullyLoaded)
|
if(!librariesSuccessfullyLoaded)
|
||||||
throw new Exception("Unable to load X11 client libraries");
|
throw new Exception("Unable to load X11 client libraries");
|
||||||
display = XOpenDisplay(displayName);
|
display = XOpenDisplay(displayName);
|
||||||
|
|
||||||
|
|
||||||
|
auto str = display.display_name;
|
||||||
|
// this is a bit of a hack but like if it looks like a unix socket we assume it is local
|
||||||
|
// and otherwise it probably isn't
|
||||||
|
if(str is null || (str[0] != ':' && str[0] != '/'))
|
||||||
|
isLocal_ = false;
|
||||||
|
else
|
||||||
|
isLocal_ = true;
|
||||||
|
|
||||||
|
|
||||||
//XSetErrorHandler(&adrlogger);
|
//XSetErrorHandler(&adrlogger);
|
||||||
//XSynchronize(display, true);
|
//XSynchronize(display, true);
|
||||||
connectionSequence_++;
|
connectionSequence_++;
|
||||||
|
@ -12511,11 +12539,7 @@ mixin DynamicLoad!(XRender, "Xrender", 1, false, true) XRenderLibrary;
|
||||||
int i1, i2, i3;
|
int i1, i2, i3;
|
||||||
xshmQueryCompleted = true;
|
xshmQueryCompleted = true;
|
||||||
|
|
||||||
auto str = XDisplayConnection.get().display_name;
|
if(!XDisplayConnection.isLocal)
|
||||||
// only if we are actually on the same machine does this
|
|
||||||
// have any hope, and the query extension only asks if
|
|
||||||
// the server can in theory, not in practice.
|
|
||||||
if(str is null || (str[0] != ':' && str[0] != '/'))
|
|
||||||
_xshmAvailable = false;
|
_xshmAvailable = false;
|
||||||
else
|
else
|
||||||
_xshmAvailable = XQueryExtension(XDisplayConnection.get(), "MIT-SHM", &i1, &i2, &i3) != 0;
|
_xshmAvailable = XQueryExtension(XDisplayConnection.get(), "MIT-SHM", &i1, &i2, &i3) != 0;
|
||||||
|
@ -12526,6 +12550,8 @@ mixin DynamicLoad!(XRender, "Xrender", 1, false, true) XRenderLibrary;
|
||||||
bool usingXshm;
|
bool usingXshm;
|
||||||
final:
|
final:
|
||||||
|
|
||||||
|
private __gshared bool xshmfailed;
|
||||||
|
|
||||||
void createImage(int width, int height, bool forcexshm=false, bool enableAlpha = false) {
|
void createImage(int width, int height, bool forcexshm=false, bool enableAlpha = false) {
|
||||||
auto display = XDisplayConnection.get();
|
auto display = XDisplayConnection.get();
|
||||||
assert(display !is null);
|
assert(display !is null);
|
||||||
|
@ -12534,6 +12560,22 @@ mixin DynamicLoad!(XRender, "Xrender", 1, false, true) XRenderLibrary;
|
||||||
// it will only use shared memory for somewhat largish images,
|
// it will only use shared memory for somewhat largish images,
|
||||||
// since otherwise we risk wasting shared memory handles on a lot of little ones
|
// since otherwise we risk wasting shared memory handles on a lot of little ones
|
||||||
if (xshmAvailable && (forcexshm || (width > 100 && height > 100))) {
|
if (xshmAvailable && (forcexshm || (width > 100 && height > 100))) {
|
||||||
|
|
||||||
|
|
||||||
|
// it is possible for the query extension to return true, the DISPLAY check to pass, yet
|
||||||
|
// the actual use still fails. For example, if the program is in a container and permission denied
|
||||||
|
// on shared memory, or if it is a local thing forwarded to a remote server, etc.
|
||||||
|
//
|
||||||
|
// If it does fail, we need to detect it now, abort the xshm and fall back to core protocol.
|
||||||
|
|
||||||
|
|
||||||
|
// synchronize so preexisting buffers are clear
|
||||||
|
XSync(display, false);
|
||||||
|
xshmfailed = false;
|
||||||
|
|
||||||
|
auto oldErrorHandler = XSetErrorHandler(&XShmErrorHandler);
|
||||||
|
|
||||||
|
|
||||||
usingXshm = true;
|
usingXshm = true;
|
||||||
handle = XShmCreateImage(
|
handle = XShmCreateImage(
|
||||||
display,
|
display,
|
||||||
|
@ -12543,22 +12585,66 @@ mixin DynamicLoad!(XRender, "Xrender", 1, false, true) XRenderLibrary;
|
||||||
null,
|
null,
|
||||||
&shminfo,
|
&shminfo,
|
||||||
width, height);
|
width, height);
|
||||||
assert(handle !is null);
|
if(handle is null)
|
||||||
|
goto abortXshm1;
|
||||||
|
|
||||||
|
if(handle.bytes_per_line != 4 * width)
|
||||||
|
goto abortXshm2;
|
||||||
|
|
||||||
assert(handle.bytes_per_line == 4 * width);
|
|
||||||
shminfo.shmid = shmget(IPC_PRIVATE, handle.bytes_per_line * height, IPC_CREAT | 511 /* 0777 */);
|
shminfo.shmid = shmget(IPC_PRIVATE, handle.bytes_per_line * height, IPC_CREAT | 511 /* 0777 */);
|
||||||
//import std.conv; import core.stdc.errno;
|
if(shminfo.shmid < 0)
|
||||||
assert(shminfo.shmid >= 0);//, to!string(errno));
|
goto abortXshm3;
|
||||||
handle.data = shminfo.shmaddr = rawData = cast(ubyte*) shmat(shminfo.shmid, null, 0);
|
handle.data = shminfo.shmaddr = rawData = cast(ubyte*) shmat(shminfo.shmid, null, 0);
|
||||||
assert(rawData != cast(ubyte*) -1);
|
if(rawData == cast(ubyte*) -1)
|
||||||
|
goto abortXshm4;
|
||||||
shminfo.readOnly = 0;
|
shminfo.readOnly = 0;
|
||||||
XShmAttach(display, &shminfo);
|
XShmAttach(display, &shminfo);
|
||||||
|
|
||||||
|
// and now to the final error check to ensure it actually worked.
|
||||||
|
XSync(display, false);
|
||||||
|
if(xshmfailed)
|
||||||
|
goto abortXshm5;
|
||||||
|
|
||||||
|
XSetErrorHandler(oldErrorHandler);
|
||||||
|
|
||||||
XDisplayConnection.registerImage(this);
|
XDisplayConnection.registerImage(this);
|
||||||
// if I don't flush here there's a chance the dtor will run before the
|
// if I don't flush here there's a chance the dtor will run before the
|
||||||
// ctor and lead to a bad value X error. While this hurts the efficiency
|
// ctor and lead to a bad value X error. While this hurts the efficiency
|
||||||
// it is local anyway so prolly better to keep it simple
|
// it is local anyway so prolly better to keep it simple
|
||||||
XFlush(display);
|
XFlush(display);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
abortXshm5:
|
||||||
|
shmdt(shminfo.shmaddr);
|
||||||
|
rawData = null;
|
||||||
|
|
||||||
|
abortXshm4:
|
||||||
|
shmctl(shminfo.shmid, IPC_RMID, null);
|
||||||
|
|
||||||
|
abortXshm3:
|
||||||
|
// nothing needed, the shmget failed so there's nothing to free
|
||||||
|
|
||||||
|
abortXshm2:
|
||||||
|
XDestroyImage(handle);
|
||||||
|
handle = null;
|
||||||
|
|
||||||
|
abortXshm1:
|
||||||
|
XSetErrorHandler(oldErrorHandler);
|
||||||
|
usingXshm = false;
|
||||||
|
handle = null;
|
||||||
|
|
||||||
|
shminfo = typeof(shminfo).init;
|
||||||
|
|
||||||
|
_xshmAvailable = false; // don't try again in the future
|
||||||
|
|
||||||
|
//import std.stdio; writeln("fallingback");
|
||||||
|
|
||||||
|
goto fallback;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
fallback:
|
||||||
|
|
||||||
if (forcexshm) throw new Exception("can't create XShm Image");
|
if (forcexshm) throw new Exception("can't create XShm Image");
|
||||||
// This actually needs to be malloc to avoid a double free error when XDestroyImage is called
|
// This actually needs to be malloc to avoid a double free error when XDestroyImage is called
|
||||||
import core.stdc.stdlib : malloc;
|
import core.stdc.stdlib : malloc;
|
||||||
|
@ -13182,7 +13268,7 @@ mixin DynamicLoad!(XRender, "Xrender", 1, false, true) XRenderLibrary;
|
||||||
;
|
;
|
||||||
|
|
||||||
// xshm is our shortcut for local connections
|
// xshm is our shortcut for local connections
|
||||||
if(Image.impl.xshmAvailable || forceIncludeMouseMotion)
|
if(XDisplayConnection.isLocal || forceIncludeMouseMotion)
|
||||||
mask |= EventMask.PointerMotionMask;
|
mask |= EventMask.PointerMotionMask;
|
||||||
else
|
else
|
||||||
mask |= EventMask.ButtonMotionMask;
|
mask |= EventMask.ButtonMotionMask;
|
||||||
|
|
Loading…
Reference in New Issue