plan9port/src/cmd/devdraw/x11-init.c
rsc 3a19470202 In non-blocking recv functions in libmux and libdraw,
distinguish between "cannot receive without blocking"
and "EOF on connection".

In libmux, do not elect async guys muxers, so that
synchronous RPC calls run in the main event loop
(e.g., in eresized) do not get stuck.

Fixes problem reported by Lu Xuxiao, namely that
jpg etc. would spin at 100% cpu usage.
2006-11-04 18:46:00 +00:00

795 lines
18 KiB
C

/*
* Some of the stuff in this file is not X-dependent and should be elsewhere.
*/
#include <u.h>
#include "x11-inc.h"
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <keyboard.h>
#include <mouse.h>
#include <cursor.h>
#include "x11-memdraw.h"
static int parsewinsize(char*, Rectangle*, int*);
static void plan9cmap(void);
static int setupcmap(XWindow);
static XGC xgc(XDrawable, int, int);
Xprivate _x;
static int
xerror(XDisplay *d, XErrorEvent *e)
{
char buf[200];
if(e->request_code == 42) /* XSetInputFocus */
return 0;
if(e->request_code == 18) /* XChangeProperty */
return 0;
/*
* BadDrawable happens in apps that get resized a LOT,
* e.g. when KDE is configured to resize continuously
* during a window drag.
*/
if(e->error_code == 9) /* BadDrawable */
return 0;
fprint(2, "X error: error_code=%d, request_code=%d, minor=%d disp=%p\n",
e->error_code, e->request_code, e->minor_code, d);
XGetErrorText(d, e->error_code, buf, sizeof buf);
fprint(2, "%s\n", buf);
return 0;
}
static int
xioerror(XDisplay *d)
{
/*print("X I/O error\n"); */
exit(0);
/*sysfatal("X I/O error\n");*/
abort();
return -1;
}
Memimage*
_xattach(char *label, char *winsize)
{
char *argv[2], *disp;
int i, havemin, height, mask, n, width, x, xrootid, y;
Rectangle r;
XClassHint classhint;
XDrawable pmid;
XPixmapFormatValues *pfmt;
XScreen *xscreen;
XSetWindowAttributes attr;
XSizeHints normalhint;
XTextProperty name;
XVisualInfo xvi;
XWindow xrootwin;
XWindowAttributes wattr;
XWMHints hint;
Atom atoms[2];
/*
if(XInitThreads() == 0){
fprint(2, "XInitThreads failed\n");
abort();
}
*/
/*
* Connect to X server.
*/
_x.display = XOpenDisplay(NULL);
if(_x.display == nil){
disp = getenv("DISPLAY");
werrstr("XOpenDisplay %s: %r", disp ? disp : ":0");
free(disp);
return nil;
}
_x.fd = ConnectionNumber(_x.display);
XSetErrorHandler(xerror);
XSetIOErrorHandler(xioerror);
xrootid = DefaultScreen(_x.display);
xrootwin = DefaultRootWindow(_x.display);
/*
* Figure out underlying screen format.
*/
if(XMatchVisualInfo(_x.display, xrootid, 16, TrueColor, &xvi)
|| XMatchVisualInfo(_x.display, xrootid, 16, DirectColor, &xvi)){
_x.vis = xvi.visual;
_x.depth = 16;
}
else
if(XMatchVisualInfo(_x.display, xrootid, 15, TrueColor, &xvi)
|| XMatchVisualInfo(_x.display, xrootid, 15, DirectColor, &xvi)){
_x.vis = xvi.visual;
_x.depth = 15;
}
else
if(XMatchVisualInfo(_x.display, xrootid, 24, TrueColor, &xvi)
|| XMatchVisualInfo(_x.display, xrootid, 24, DirectColor, &xvi)){
_x.vis = xvi.visual;
_x.depth = 24;
}
else
if(XMatchVisualInfo(_x.display, xrootid, 8, PseudoColor, &xvi)
|| XMatchVisualInfo(_x.display, xrootid, 8, StaticColor, &xvi)){
if(_x.depth > 8){
werrstr("can't deal with colormapped depth %d screens",
_x.depth);
goto err0;
}
_x.vis = xvi.visual;
_x.depth = 8;
}
else{
_x.depth = DefaultDepth(_x.display, xrootid);
if(_x.depth != 8){
werrstr("can't understand depth %d screen", _x.depth);
goto err0;
}
_x.vis = DefaultVisual(_x.display, xrootid);
}
if(DefaultDepth(_x.display, xrootid) == _x.depth)
_x.usetable = 1;
/*
* _x.depth is only the number of significant pixel bits,
* not the total number of pixel bits. We need to walk the
* display list to find how many actual bits are used
* per pixel.
*/
_x.chan = 0;
pfmt = XListPixmapFormats(_x.display, &n);
for(i=0; i<n; i++){
if(pfmt[i].depth == _x.depth){
switch(pfmt[i].bits_per_pixel){
case 1: /* untested */
_x.chan = GREY1;
break;
case 2: /* untested */
_x.chan = GREY2;
break;
case 4: /* untested */
_x.chan = GREY4;
break;
case 8:
_x.chan = CMAP8;
break;
case 15:
_x.chan = RGB15;
break;
case 16: /* how to tell RGB15? */
_x.chan = RGB16;
break;
case 24: /* untested (impossible?) */
_x.chan = RGB24;
break;
case 32:
_x.chan = XRGB32;
break;
}
}
}
if(_x.chan == 0){
werrstr("could not determine screen pixel format");
goto err0;
}
/*
* Set up color map if necessary.
*/
xscreen = DefaultScreenOfDisplay(_x.display);
_x.cmap = DefaultColormapOfScreen(xscreen);
if(_x.vis->class != StaticColor){
plan9cmap();
setupcmap(xrootwin);
}
/*
* We get to choose the initial rectangle size.
* This is arbitrary. In theory we should read the
* command line and allow the traditional X options.
*/
mask = 0;
x = 0;
y = 0;
if(winsize && winsize[0]){
if(parsewinsize(winsize, &r, &havemin) < 0)
sysfatal("%r");
}else{
/*
* Parse the various X resources. Thanks to Peter Canning.
*/
char *screen_resources, *display_resources, *geom,
*geomrestype, *home, *file;
XrmDatabase database;
XrmValue geomres;
database = XrmGetDatabase(_x.display);
screen_resources = XScreenResourceString(xscreen);
if(screen_resources != nil){
XrmCombineDatabase(XrmGetStringDatabase(screen_resources), &database, False);
XFree(screen_resources);
}
display_resources = XResourceManagerString(_x.display);
if(display_resources == nil){
home = getenv("HOME");
if(home!=nil && (file=smprint("%s/.Xdefaults", home)) != nil){
XrmCombineFileDatabase(file, &database, False);
free(file);
}
free(home);
}else
XrmCombineDatabase(XrmGetStringDatabase(display_resources), &database, False);
geom = smprint("%s.geometry", label);
if(geom && XrmGetResource(database, geom, nil, &geomrestype, &geomres))
mask = XParseGeometry(geomres.addr, &x, &y, (uint*)&width, (uint*)&height);
free(geom);
if((mask & WidthValue) && (mask & HeightValue)){
r = Rect(0, 0, width, height);
}else{
r = Rect(0, 0, WidthOfScreen(xscreen)*3/4,
HeightOfScreen(xscreen)*3/4);
if(Dx(r) > Dy(r)*3/2)
r.max.x = r.min.x + Dy(r)*3/2;
if(Dy(r) > Dx(r)*3/2)
r.max.y = r.min.y + Dx(r)*3/2;
}
if(mask & XNegative){
x += WidthOfScreen(xscreen);
}
if(mask & YNegative){
y += HeightOfScreen(xscreen);
}
havemin = 0;
}
memset(&attr, 0, sizeof attr);
attr.colormap = _x.cmap;
attr.background_pixel = ~0;
attr.border_pixel = 0;
_x.drawable = XCreateWindow(
_x.display, /* display */
xrootwin, /* parent */
x, /* x */
y, /* y */
Dx(r), /* width */
Dy(r), /* height */
0, /* border width */
_x.depth, /* depth */
InputOutput, /* class */
_x.vis, /* visual */
/* valuemask */
CWBackPixel|CWBorderPixel|CWColormap,
&attr /* attributes (the above aren't?!) */
);
/*
* Label and other properties required by ICCCCM.
*/
memset(&name, 0, sizeof name);
if(label == nil)
label = "pjw-face-here";
name.value = (uchar*)label;
name.encoding = XA_STRING;
name.format = 8;
name.nitems = strlen((char*)name.value);
memset(&normalhint, 0, sizeof normalhint);
normalhint.flags = PSize|PMaxSize;
if(winsize && winsize[0]){
normalhint.flags &= ~PSize;
normalhint.flags |= USSize;
normalhint.width = Dx(r);
normalhint.height = Dy(r);
}else{
if((mask & WidthValue) && (mask & HeightValue)){
normalhint.flags &= ~PSize;
normalhint.flags |= USSize;
normalhint.width = width;
normalhint.height = height;
}
if((mask & WidthValue) && (mask & HeightValue)){
normalhint.flags |= USPosition;
normalhint.x = x;
normalhint.y = y;
}
}
normalhint.max_width = WidthOfScreen(xscreen);
normalhint.max_height = HeightOfScreen(xscreen);
memset(&hint, 0, sizeof hint);
hint.flags = InputHint|StateHint;
hint.input = 1;
hint.initial_state = NormalState;
memset(&classhint, 0, sizeof classhint);
classhint.res_name = label;
classhint.res_class = label;
argv[0] = label;
argv[1] = nil;
XSetWMProperties(
_x.display, /* display */
_x.drawable, /* window */
&name, /* XA_WM_NAME property */
&name, /* XA_WM_ICON_NAME property */
argv, /* XA_WM_COMMAND */
1, /* argc */
&normalhint, /* XA_WM_NORMAL_HINTS */
&hint, /* XA_WM_HINTS */
&classhint /* XA_WM_CLASSHINTS */
);
XFlush(_x.display);
if(havemin){
XWindowChanges ch;
memset(&ch, 0, sizeof ch);
ch.x = r.min.x;
ch.y = r.min.y;
XConfigureWindow(_x.display, _x.drawable, CWX|CWY, &ch);
/*
* Must pretend origin is 0,0 for X.
*/
r = Rect(0,0,Dx(r),Dy(r));
}
/*
* Look up clipboard atom.
*/
_x.clipboard = XInternAtom(_x.display, "CLIPBOARD", False);
_x.utf8string = XInternAtom(_x.display, "UTF8_STRING", False);
_x.targets = XInternAtom(_x.display, "TARGETS", False);
_x.text = XInternAtom(_x.display, "TEXT", False);
_x.compoundtext = XInternAtom(_x.display, "COMPOUND_TEXT", False);
_x.takefocus = XInternAtom(_x.display, "WM_TAKE_FOCUS", False);
_x.losefocus = XInternAtom(_x.display, "_9WM_LOSE_FOCUS", False);
_x.wmprotos = XInternAtom(_x.display, "WM_PROTOCOLS", False);
atoms[0] = _x.takefocus;
atoms[1] = _x.losefocus;
XChangeProperty(_x.display, _x.drawable, _x.wmprotos, XA_ATOM, 32,
PropModeReplace, (uchar*)atoms, 2);
/*
* Put the window on the screen, check to see what size we actually got.
*/
XMapWindow(_x.display, _x.drawable);
XSync(_x.display, False);
if(!XGetWindowAttributes(_x.display, _x.drawable, &wattr))
fprint(2, "XGetWindowAttributes failed\n");
else if(wattr.width && wattr.height){
if(wattr.width != Dx(r) || wattr.height != Dy(r)){
r.max.x = wattr.width;
r.max.y = wattr.height;
}
}else
fprint(2, "XGetWindowAttributes: bad attrs\n");
/*
* Allocate our local backing store.
*/
_x.screenr = r;
_x.screenpm = XCreatePixmap(_x.display, _x.drawable, Dx(r), Dy(r), _x.depth);
_x.nextscreenpm = _x.screenpm;
_x.screenimage = _xallocmemimage(r, _x.chan, _x.screenpm);
/*
* Allocate some useful graphics contexts for the future.
*/
_x.gcfill = xgc(_x.screenpm, FillSolid, -1);
_x.gccopy = xgc(_x.screenpm, -1, -1);
_x.gcsimplesrc = xgc(_x.screenpm, FillStippled, -1);
_x.gczero = xgc(_x.screenpm, -1, -1);
_x.gcreplsrc = xgc(_x.screenpm, FillTiled, -1);
pmid = XCreatePixmap(_x.display, _x.drawable, 1, 1, 1);
_x.gcfill0 = xgc(pmid, FillSolid, 0);
_x.gccopy0 = xgc(pmid, -1, -1);
_x.gcsimplesrc0 = xgc(pmid, FillStippled, -1);
_x.gczero0 = xgc(pmid, -1, -1);
_x.gcreplsrc0 = xgc(pmid, FillTiled, -1);
XFreePixmap(_x.display, pmid);
return _x.screenimage;
err0:
/*
* Should do a better job of cleaning up here.
*/
XCloseDisplay(_x.display);
return nil;
}
int
_xsetlabel(char *label)
{
XTextProperty name;
/*
* Label and other properties required by ICCCCM.
*/
memset(&name, 0, sizeof name);
if(label == nil)
label = "pjw-face-here";
name.value = (uchar*)label;
name.encoding = XA_STRING;
name.format = 8;
name.nitems = strlen((char*)name.value);
XSetWMProperties(
_x.display, /* display */
_x.drawable, /* window */
&name, /* XA_WM_NAME property */
&name, /* XA_WM_ICON_NAME property */
nil, /* XA_WM_COMMAND */
0, /* argc */
nil, /* XA_WM_NORMAL_HINTS */
nil, /* XA_WM_HINTS */
nil /* XA_WM_CLASSHINTS */
);
XFlush(_x.display);
return 0;
}
/*
* Create a GC with a particular fill style and XXX.
* Disable generation of GraphicsExpose/NoExpose events in the GC.
*/
static XGC
xgc(XDrawable d, int fillstyle, int foreground)
{
XGC gc;
XGCValues v;
memset(&v, 0, sizeof v);
v.function = GXcopy;
v.graphics_exposures = False;
gc = XCreateGC(_x.display, d, GCFunction|GCGraphicsExposures, &v);
if(fillstyle != -1)
XSetFillStyle(_x.display, gc, fillstyle);
if(foreground != -1)
XSetForeground(_x.display, gc, 0);
return gc;
}
/*
* Initialize map with the Plan 9 rgbv color map.
*/
static void
plan9cmap(void)
{
int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7;
static int once;
if(once)
return;
once = 1;
for(r=0; r!=4; r++)
for(g = 0; g != 4; g++)
for(b = 0; b!=4; b++)
for(v = 0; v!=4; v++){
den=r;
if(g > den)
den=g;
if(b > den)
den=b;
/* divide check -- pick grey shades */
if(den==0)
cr=cg=cb=v*17;
else {
num=17*(4*den+v);
cr=r*num/den;
cg=g*num/den;
cb=b*num/den;
}
idx = r*64 + v*16 + ((g*4 + b + v - r) & 15);
_x.map[idx].red = cr*0x0101;
_x.map[idx].green = cg*0x0101;
_x.map[idx].blue = cb*0x0101;
_x.map[idx].pixel = idx;
_x.map[idx].flags = DoRed|DoGreen|DoBlue;
v7 = v >> 1;
idx7 = r*32 + v7*16 + g*4 + b;
if((v & 1) == v7){
_x.map7to8[idx7][0] = idx;
if(den == 0) { /* divide check -- pick grey shades */
cr = ((255.0/7.0)*v7)+0.5;
cg = cr;
cb = cr;
}
else {
num=17*15*(4*den+v7*2)/14;
cr=r*num/den;
cg=g*num/den;
cb=b*num/den;
}
_x.map7[idx7].red = cr*0x0101;
_x.map7[idx7].green = cg*0x0101;
_x.map7[idx7].blue = cb*0x0101;
_x.map7[idx7].pixel = idx7;
_x.map7[idx7].flags = DoRed|DoGreen|DoBlue;
}
else
_x.map7to8[idx7][1] = idx;
}
}
/*
* Initialize and install the rgbv color map as a private color map
* for this application. It gets the best colors when it has the
* cursor focus.
*
* We always choose the best depth possible, but that might not
* be the default depth. On such "suboptimal" systems, we have to allocate an
* empty color map anyway, according to Axel Belinfante.
*/
static int
setupcmap(XWindow w)
{
char buf[30];
int i;
u32int p, pp;
XColor c;
if(_x.depth <= 1)
return 0;
if(_x.depth >= 24) {
if(_x.usetable == 0)
_x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocNone);
/*
* The pixel value returned from XGetPixel needs to
* be converted to RGB so we can call rgb2cmap()
* to translate between 24 bit X and our color. Unfortunately,
* the return value appears to be display server endian
* dependant. Therefore, we run some heuristics to later
* determine how to mask the int value correctly.
* Yeah, I know we can look at _x.vis->byte_order but
* some displays say MSB even though they run on LSB.
* Besides, this is more anal.
*/
c = _x.map[19]; /* known to have different R, G, B values */
if(!XAllocColor(_x.display, _x.cmap, &c)){
werrstr("XAllocColor: %r");
return -1;
}
p = c.pixel;
pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff);
if(pp != _x.map[19].pixel) {
/* check if endian is other way */
pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff);
if(pp != _x.map[19].pixel){
werrstr("cannot detect X server byte order");
return -1;
}
switch(_x.chan){
case RGB24:
_x.chan = BGR24;
break;
case XRGB32:
_x.chan = XBGR32;
break;
default:
werrstr("cannot byteswap channel %s",
chantostr(buf, _x.chan));
break;
}
}
}else if(_x.vis->class == TrueColor || _x.vis->class == DirectColor){
/*
* Do nothing. We have no way to express a
* mixed-endian 16-bit screen, so pretend they don't exist.
*/
if(_x.usetable == 0)
_x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocNone);
}else if(_x.vis->class == PseudoColor){
if(_x.usetable == 0){
_x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocAll);
XStoreColors(_x.display, _x.cmap, _x.map, 256);
for(i = 0; i < 256; i++){
_x.tox11[i] = i;
_x.toplan9[i] = i;
}
}else{
for(i = 0; i < 128; i++){
c = _x.map7[i];
if(!XAllocColor(_x.display, _x.cmap, &c)){
werrstr("can't allocate colors in 7-bit map");
return -1;
}
_x.tox11[_x.map7to8[i][0]] = c.pixel;
_x.tox11[_x.map7to8[i][1]] = c.pixel;
_x.toplan9[c.pixel] = _x.map7to8[i][0];
}
}
}else{
werrstr("unsupported visual class %d", _x.vis->class);
return -1;
}
return 0;
}
void
_flushmemscreen(Rectangle r)
{
if(_x.nextscreenpm != _x.screenpm){
qlock(&_x.screenlock);
XSync(_x.display, False);
XFreePixmap(_x.display, _x.screenpm);
_x.screenpm = _x.nextscreenpm;
qunlock(&_x.screenlock);
}
if(r.min.x >= r.max.x || r.min.y >= r.max.y)
return;
XCopyArea(_x.display, _x.screenpm, _x.drawable, _x.gccopy, r.min.x, r.min.y,
Dx(r), Dy(r), r.min.x, r.min.y);
XFlush(_x.display);
}
void
_xexpose(XEvent *e)
{
XExposeEvent *xe;
Rectangle r;
qlock(&_x.screenlock);
if(_x.screenpm != _x.nextscreenpm){
qunlock(&_x.screenlock);
return;
}
xe = (XExposeEvent*)e;
r.min.x = xe->x;
r.min.y = xe->y;
r.max.x = xe->x+xe->width;
r.max.y = xe->y+xe->height;
XCopyArea(_x.display, _x.screenpm, _x.drawable, _x.gccopy, r.min.x, r.min.y,
Dx(r), Dy(r), r.min.x, r.min.y);
XSync(_x.display, False);
qunlock(&_x.screenlock);
}
int
_xdestroy(XEvent *e)
{
XDestroyWindowEvent *xe;
xe = (XDestroyWindowEvent*)e;
if(xe->window == _x.drawable){
_x.destroyed = 1;
return 1;
}
return 0;
}
int
_xconfigure(XEvent *e)
{
Rectangle r;
XConfigureEvent *xe = (XConfigureEvent*)e;
if(xe->width == Dx(_x.screenr) && xe->height == Dy(_x.screenr))
return 0;
r = Rect(0, 0, xe->width, xe->height);
qlock(&_x.screenlock);
if(_x.screenpm != _x.nextscreenpm){
XCopyArea(_x.display, _x.screenpm, _x.drawable, _x.gccopy, r.min.x, r.min.y,
Dx(r), Dy(r), r.min.x, r.min.y);
XSync(_x.display, False);
}
qunlock(&_x.screenlock);
_x.newscreenr = r;
return 1;
}
int
_xreplacescreenimage(void)
{
Memimage *m;
XDrawable pixmap;
Rectangle r;
r = _x.newscreenr;
if(eqrect(_x.screenr, r))
return 0;
pixmap = XCreatePixmap(_x.display, _x.drawable, Dx(r), Dy(r), _x.depth);
m = _xallocmemimage(r, _x.chan, pixmap);
if(_x.nextscreenpm != _x.screenpm)
XFreePixmap(_x.display, _x.nextscreenpm);
_x.nextscreenpm = pixmap;
_x.screenr = r;
_drawreplacescreenimage(m);
return 1;
}
static int
parsewinsize(char *s, Rectangle *r, int *havemin)
{
char c, *os;
int i, j, k, l;
os = s;
*havemin = 0;
*r = Rect(0,0,0,0);
if(!isdigit((uchar)*s))
goto oops;
i = strtol(s, &s, 0);
if(*s == 'x'){
s++;
if(!isdigit((uchar)*s))
goto oops;
j = strtol(s, &s, 0);
r->max.x = i;
r->max.y = j;
if(*s == 0)
return 0;
if(*s != '@')
goto oops;
s++;
if(!isdigit((uchar)*s))
goto oops;
i = strtol(s, &s, 0);
if(*s != ',' && *s != ' ')
goto oops;
s++;
if(!isdigit((uchar)*s))
goto oops;
j = strtol(s, &s, 0);
if(*s != 0)
goto oops;
*r = rectaddpt(*r, Pt(i,j));
*havemin = 1;
return 0;
}
c = *s;
if(c != ' ' && c != ',')
goto oops;
s++;
if(!isdigit((uchar)*s))
goto oops;
j = strtol(s, &s, 0);
if(*s != c)
goto oops;
s++;
if(!isdigit((uchar)*s))
goto oops;
k = strtol(s, &s, 0);
if(*s != c)
goto oops;
s++;
if(!isdigit((uchar)*s))
goto oops;
l = strtol(s, &s, 0);
if(*s != 0)
goto oops;
*r = Rect(i,j,k,l);
*havemin = 1;
return 0;
oops:
werrstr("bad syntax in window size '%s'", os);
return -1;
}