/** ** vd_xwin.c ---- the standard X Window driver ** ** Author: Ulrich Leodolter ** E-mail: ulrich@lab1.psy.univie.ac.at ** Date: Thu Sep 28 09:29:26 1995 ** RCSId: $Id: vd_xwin.c 1.2 1995/11/19 19:28:12 ulrich Exp $ ** ** This file is part of the GRX graphics library. ** ** The GRX graphics library is free software; you can redistribute it ** and/or modify it under some conditions; see the "copying.grx" file ** for details. ** ** This library is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** ** Contributions by: ** 070505 M.Alvarez, Using a Pixmap for BackingStore ** 071201 M.Alvarez, Added videomodes for wide monitors ** 071201 M.Alvarez, The modes higher than the X resolution ** are made 'non-present' ** 071201 M.Alvarez, go to fullscreen if w,h == X resolution ** GR_biggest_graphics is honored ** 170714 M.Alvarez, Use BStore only if not generated GREV_EXPOSE events ** 191022 M.Alvarez, Added SubstructureRedirectMask to go/return from fullsecreen ** 191112 M.Alvarez, Added code to generate GREV_WMEND events ** 211110 M.Alvarez, support window resize ** 211206 M.Alvarez, when window resizing is active create a big backing store and ** not recreate it in every setmode, so resizing is smooth **/ #include "libgrx.h" #include "libxwin.h" #include "grdriver.h" #include "arith.h" Display * _XGrDisplay = NULL; int _XGrScreen; Window _XGrWindow = None; Pixmap _XGrBitmap; Pixmap _XGrPattern; Pixmap _XGrBStore; Drawable _XGRActDrawable; Colormap _XGrColormap; GC _XGrGC; GC _XGrBitmapGC; GC _XGrPatternGC; GC _XGrDefaultGC; XIM _XGrXim = NULL; XIC _XGrXic = NULL; unsigned long _XGrForeColor; unsigned long _XGrBackColor; unsigned int _XGrColorOper; unsigned int _XGrDepth; unsigned int _XGrBitsPerPixel; unsigned int _XGrScanlinePad; unsigned int _XGrMaxWidth; unsigned int _XGrMaxHeight; unsigned int _XGrCurWidth; unsigned int _XGrCurHeight; int _XGrBStoreInited = 0; int _XGrFullScreen = 0; int _XGrWMaped = 0; int _XGrGenExposeEvents = GR_GEN_EXPOSE_NO; int _XGrGenWMEndEvents = GR_GEN_WMEND_NO; int _XGrGenWSzChgEvents = FALSE; Atom _wmDeleteWindow; int _XGrUserHadSetWName = 0; unsigned long _XGrColorPlanes[8]; unsigned int _XGrColorNumPlanes; unsigned long _XGrColorPixels[2]; unsigned int _XGrColorNumPixels; char *_XGrClassNames[6] = { "StaticGray", "GrayScale", "StaticColor", "PseudoColor", "TrueColor", "DirectColor" }; static void setbank(int bk); static void setrwbanks(int rb, int wb); static void loadcolor(int c, int r, int g, int b); static int setmode(GrVideoMode *mp, int noclear); GrVideoModeExt grtextext = { GR_frameText, /* frame driver */ NULL, /* frame driver override */ NULL, /* frame buffer address */ { 0, 0, 0 }, /* color precisions */ { 0, 0, 0 }, /* color component bit positions */ 0, /* mode flag bits */ setmode, /* mode set */ NULL, /* virtual size set */ NULL, /* virtual scroll */ NULL, /* bank set function */ NULL, /* double bank set function */ NULL /* color loader */ }; static GrVideoModeExt grxwinext = { GR_frameUndef, /* frame driver */ NULL, /* frame driver override */ NULL, /* frame buffer address */ { 0, 0, 0 }, /* color precisions */ { 0, 0, 0 }, /* color component bit positions */ 0, /* mode flag bits */ setmode, /* mode set */ NULL, /* virtual size set */ NULL, /* virtual scroll */ setbank, /* bank set function */ setrwbanks, /* double bank set */ loadcolor /* color loader */ }; static GrVideoMode modes[] = { /* pres. bpp wdt hgt BIOS scan priv. &ext */ { TRUE, 8, 80, 25, 0x00, 80, 1, &grtextext }, { FALSE, 0, 320, 240, 0x00, 0, 0, &grxwinext }, { FALSE, 0, 640, 480, 0x00, 0, 0, &grxwinext }, { FALSE, 0, 800, 600, 0x00, 0, 0, &grxwinext }, { FALSE, 0, 1024, 768, 0x00, 0, 0, &grxwinext }, { FALSE, 0, 1280, 1024, 0x00, 0, 0, &grxwinext }, { FALSE, 0, 1600, 1200, 0x00, 0, 0, &grxwinext }, { FALSE, 0, 1440, 900, 0x00, 0, 0, &grxwinext }, { FALSE, 0, 1680, 1050, 0x00, 0, 0, &grxwinext }, { FALSE, 0, 1920, 1200, 0x00, 0, 0, &grxwinext }, { FALSE, 0, 2560, 1440, 0x00, 0, 0, &grxwinext }, { FALSE, 0, 3840, 2160, 0x00, 0, 0, &grxwinext }, { FALSE, 0, 9999, 9999, 0x00, 0, 0, &grxwinext } }; static void goto_fullscreen(Display *dsp, Window win) { XEvent xev; Atom wm_state = XInternAtom(dsp, "_NET_WM_STATE", False); Atom fullscreen = XInternAtom(dsp, "_NET_WM_STATE_FULLSCREEN", False); memset(&xev, 0, sizeof(xev)); xev.type = ClientMessage; xev.xclient.window = win; xev.xclient.message_type = wm_state; xev.xclient.format = 32; xev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD xev.xclient.data.l[1] = fullscreen; xev.xclient.data.l[2] = 0; XSendEvent(dsp, DefaultRootWindow(dsp), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev); } static void returnfrom_fullscreen(Display *dsp, Window win) { XEvent xev; Atom wm_state = XInternAtom(dsp, "_NET_WM_STATE", False); Atom fullscreen = XInternAtom(dsp, "_NET_WM_STATE_FULLSCREEN", False); memset(&xev, 0, sizeof(xev)); xev.type = ClientMessage; xev.xclient.window = win; xev.xclient.message_type = wm_state; xev.xclient.format = 32; xev.xclient.data.l[0] = 0; // _NET_WM_STATE_REMOVE xev.xclient.data.l[1] = fullscreen; xev.xclient.data.l[2] = 0; XSendEvent(dsp, DefaultRootWindow(dsp), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev); } static void setbank(int bk) {} static void setrwbanks(int rb, int wb) {} static void loadcolor(int c, int r, int g, int b) { GRX_ENTER(); if (_XGrDisplay != NULL && _XGrColormap != None && !(_XGrColorNumPixels == 1 && (c < _XGrColorPixels[0] || c > _XGrColorPixels[1])) ) { XColor xcolor; DBGPRINTF(DBG_DRIVER,("Setting X11 color %d to r=%d, g=%d, b=%d\n",c,r,g,b)); xcolor.pixel = c; xcolor.red = r * 257; xcolor.green = g * 257; xcolor.blue = b * 257; xcolor.flags = DoRed|DoGreen|DoBlue; XStoreColor(_XGrDisplay, _XGrColormap, &xcolor); } GRX_LEAVE(); } static int setmode(GrVideoMode *mp, int noclear) { XEvent xevent; int res = FALSE; GRX_ENTER(); if (_XGrDisplay == NULL || _XGrWindow == None) goto done; //printf("setmode %d %d\n", mp->width, mp->height); if (_XGrFullScreen) { returnfrom_fullscreen(_XGrDisplay, _XGrWindow); _XGrFullScreen = 0; } if (_XGrWMaped && (!_XGrGenWSzChgEvents || mp->extinfo->mode == GR_frameText)) { XUnmapWindow(_XGrDisplay, _XGrWindow); _XGrWMaped = 0; } if (_XGrBStoreInited && !_XGrWMaped) { XFreePixmap(_XGrDisplay, _XGrBStore); _XGrBStoreInited = 0; } if (mp->extinfo->mode != GR_frameText) { XSizeHints *hints; char name[100]; if (!_XGrBStoreInited) { _XGrGenExposeEvents = GR_GEN_EXPOSE_NO; if (!_XGrGenWSzChgEvents) { _XGrBStore = XCreatePixmap(_XGrDisplay, _XGrWindow, mp->width, mp->height, _XGrDepth); XFillRectangle(_XGrDisplay, _XGrBStore, _XGrGC, 0, 0, mp->width, mp->height); } else { _XGrBStore = XCreatePixmap(_XGrDisplay, _XGrWindow, _XGrMaxWidth, _XGrMaxHeight, _XGrDepth); XFillRectangle(_XGrDisplay, _XGrBStore, _XGrGC, 0, 0, _XGrMaxWidth, _XGrMaxHeight); } _XGRActDrawable = (Drawable) _XGrBStore; _XGrBStoreInited = 1; } //here because I used it in some tests, don't uncomment //XFillRectangle(_XGrDisplay, _XGrBStore, _XGrGC, 0, 0, mp->width, mp->height); //XFillRectangle(_XGrDisplay, _XGrWindow, _XGrGC, 0, 0, mp->width, mp->height); //XCopyArea(_XGrDisplay, _XGrWindow, _XGrBStore, // _XGrDefaultGC, 0, 0, mp->width, mp->height, 0, 0); //_XGRActDrawable = (Drawable *) &_XGrWindow; grxwinext.frame = (char *) &_XGRActDrawable; if (!_XGrUserHadSetWName) { sprintf (name, "mgrx %dx%dx%d", mp->width, mp->height, mp->bpp); XStoreName(_XGrDisplay, _XGrWindow, name); XSetIconName(_XGrDisplay, _XGrWindow, name); } if (!_XGrWMaped) { hints = XAllocSizeHints(); if (hints) { hints->min_width = mp->width; hints->min_height = mp->height; hints->max_width = mp->width; hints->max_height = mp->height; hints->flags = PMaxSize | PMinSize; XSetWMNormalHints(_XGrDisplay, _XGrWindow, hints); } XResizeWindow(_XGrDisplay, _XGrWindow, mp->width, mp->height); XMapRaised(_XGrDisplay, _XGrWindow); _XGrWMaped = 1; /* Wait until window appears on screen */ while (1) { XNextEvent(_XGrDisplay, &xevent); if (xevent.type == MapNotify) break; } if ((mp->width == _XGrMaxWidth) && (mp->height == _XGrMaxHeight)) { // Go to fullscreen if w,h == X resolution goto_fullscreen(_XGrDisplay, _XGrWindow); _XGrFullScreen = 1; } else if (hints) { // Important we must do this after the window is showed, // if not some WM resize back again (like the kde one) if (_XGrGenWSzChgEvents) { hints->min_width = DRVINFO->minwgw; hints->min_height = DRVINFO->minwgh; hints->max_width = _XGrMaxWidth; hints->max_height = _XGrMaxHeight; hints->flags = PMaxSize | PMinSize; XSetWMNormalHints(_XGrDisplay, _XGrWindow, hints); XResizeWindow(_XGrDisplay, _XGrWindow, mp->width, mp->height); } } if (hints) XFree(hints); } _XGrCurWidth = mp->width; _XGrCurHeight = mp->height; } res = TRUE; done: GRX_RETURN(res); } static int (*previous_error_handler)(Display *dpy, XErrorEvent *ev) = NULL; static int error_handler(Display *dpy, XErrorEvent *ev) { char buffer[200]; /* * This error is triggerd in fd_xwin.c:bltv2r() if the source rectangle * is not fully contained in the root window */ if (ev->request_code == X_GetImage && ev->error_code == BadMatch) return 0; if (previous_error_handler) return (*previous_error_handler) (dpy, ev); XGetErrorText(dpy, ev->error_code, buffer, sizeof(buffer)), fprintf(stderr, "MGRX: XError: %s XID=%ld request_code=%d serial=%lu\n", buffer, ev->resourceid, ev->request_code, ev->serial); return 0; } static GrVideoMode * _xw_selectmode( GrVideoDriver * drv, int w, int h, int bpp, int txt, unsigned int * ep ) { GrVideoMode *mp, *res; GRX_ENTER(); if (txt) { res = _gr_selectmode(drv, w, h, bpp, txt, ep); goto done; } for (mp = &modes[1]; mp < &modes[itemsof(modes)-1]; mp++) { if ( mp->present && mp->width == w && mp->height == h) { res = _gr_selectmode (drv,w,h,bpp,txt,ep); goto done; } } /* no predefined mode found. Create a new mode */ mp->present = TRUE; mp->width = (w > _XGrMaxWidth) ? _XGrMaxWidth : w; mp->height = (h > _XGrMaxHeight) ? _XGrMaxHeight : h; mp->lineoffset = (mp->width * mp->bpp) / 8; res = _gr_selectmode (drv,w,h,bpp,txt,ep); done: GRX_RETURN(res); } static int detect(void) { int res; GRX_ENTER(); res = (getenv("DISPLAY") ? TRUE : FALSE); GRX_RETURN(res); } static int init(char *options) { Window root; XSetWindowAttributes attr; unsigned long mask; Visual *visual; XPixmapFormatValues *pfmt; int pfmt_count; GrVideoMode *mp; unsigned int depth, bpp, pad; int private_colormap = FALSE; int i, j, res = FALSE; GRX_ENTER(); _XGrDisplay = XOpenDisplay (""); if (_XGrDisplay == NULL) goto done; /* FALSE */ if (options) { char opt[101] = {0}; char *token; strncpy(opt, options, 100); token = strtok(opt, ":"); while (token != NULL) { if (strncmp ("privcmap", token, 8) == 0) private_colormap = TRUE; if (strncmp ("rszwin", token, 6) == 0) _XGrGenWSzChgEvents = TRUE; token = strtok(NULL, ":"); } } if (_XGrGenWSzChgEvents) { _GrVideoDriverXWIN.drvflags |= GR_DRIVERF_WINDOW_RESIZE; } previous_error_handler = XSetErrorHandler(error_handler); _XGrScreen = DefaultScreen(_XGrDisplay); _XGrDepth = depth = DefaultDepth(_XGrDisplay, _XGrScreen); _XGrMaxWidth = DisplayWidth(_XGrDisplay, _XGrScreen); _XGrMaxHeight = DisplayHeight(_XGrDisplay, _XGrScreen); // TODO //_XGrMaxWinWidth = min(_XGrMaxWidth, DRVINFO->biggw); //_XGrMaxWinHeight = min(_XGrMaxHeight, DRVINFO->biggh); visual = DefaultVisual(_XGrDisplay, _XGrScreen); root = RootWindow(_XGrDisplay, _XGrScreen); _XGrColorNumPlanes = 0; _XGrColorNumPixels = 0; if (visual->class == PseudoColor && (depth == 4 || depth == 8)) { if (!private_colormap) { /* Allocate at least 4 planes (= 16 colors) of writable cells */ _XGrColormap = DefaultColormap(_XGrDisplay, _XGrScreen); for (i = depth - 1; i >= 4; i--) { if (XAllocColorCells(_XGrDisplay, _XGrColormap, True, /* contiguous */ _XGrColorPlanes, i, _XGrColorPixels, 1)) { _XGrColorNumPlanes = i; _XGrColorNumPixels = 1; _GR_firstFreeColor = _XGrColorPixels[0]; _XGrColorPixels[1] = _XGrColorPixels[0]; for (j = 0; j < _XGrColorNumPlanes; j++) { _XGrColorPixels[1] |= _XGrColorPlanes[j]; } _GR_lastFreeColor = _XGrColorPixels[1]-1; break; } } if (_XGrColorNumPlanes == 0) private_colormap = TRUE; } if (private_colormap) { _XGrColormap = XCreateColormap(_XGrDisplay, root, visual, AllocAll); } grxwinext.cprec[0] = visual->bits_per_rgb; grxwinext.cprec[1] = visual->bits_per_rgb; grxwinext.cprec[2] = visual->bits_per_rgb; grxwinext.cpos[0] = 0; grxwinext.cpos[1] = 0; grxwinext.cpos[2] = 0; /* loadcolor (0, 0, 0, 0); */ /* load black */ } else if (visual->class == TrueColor && (depth == 8 || depth == 15 || depth == 16 || depth == 24)) { int i, pos, prec; unsigned long mask[3]; _XGrColormap = (Colormap)0; mask[0] = visual->red_mask; mask[1] = visual->green_mask; mask[2] = visual->blue_mask; for (i = 0; i < 3; i++) { for (pos = 0; !(mask[i] & 1); pos++, mask[i]>>=1); for (prec = 0; (mask[i] & 1); prec++, mask[i]>>=1); grxwinext.cprec[i] = prec; grxwinext.cpos[i] = pos; } } else { XCloseDisplay(_XGrDisplay); fprintf(stderr, "X11 driver init: Visual class=%s depth=%d not supported\n", _XGrClassNames[visual->class], depth); goto done; } _XGrForeColor = GrNOCOLOR; /* force XSetForeground */ _XGrBackColor = GrNOCOLOR; /* force XSetBackground */ _XGrColorOper = C_WRITE; mask = 0L; attr.backing_store = NotUseful; mask |= CWBackingStore; if (_XGrColormap) { attr.colormap = _XGrColormap; mask |= CWColormap; } // I think is better to have this undefined, so in windows resizes the WM // doesn't apply background and the transition is smothy //attr.background_pixel = 0L; //mask |= CWBackPixel; //attr.border_pixel = 0L; //mask |= CWBorderPixel; attr.event_mask = (StructureNotifyMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | ExposureMask | PointerMotionMask); mask |= CWEventMask; _XGrWindow = XCreateWindow(_XGrDisplay, root, /* parent */ 0, /* x */ 0, /* y */ 1, /* width */ 1, /* height */ 0, /* border_width */ depth, /* depth */ InputOutput, /* class */ visual, /* visual */ mask, /* valuemask */ &attr); /* attributes */ _XGrBitmap = XCreatePixmap(_XGrDisplay, root, 128, 128, 1); _XGrBitmapGC = XCreateGC(_XGrDisplay, _XGrBitmap, 0L, NULL); _XGrPattern = XCreatePixmap(_XGrDisplay, root, 8, 1, 1); _XGrPatternGC = XCreateGC(_XGrDisplay, _XGrPattern, 0L, NULL); _XGrDefaultGC = DefaultGC(_XGrDisplay, _XGrScreen); _XGrGC = XCreateGC (_XGrDisplay, _XGrWindow, 0L, NULL); _XGrWMaped = 0; _wmDeleteWindow = XInternAtom(_XGrDisplay, "WM_DELETE_WINDOW", True); XSetWMProtocols(_XGrDisplay, _XGrWindow, &_wmDeleteWindow, 1); /* is this required ?? */ if (_XGrColormap) { XInstallColormap(_XGrDisplay, _XGrColormap); XSetWindowColormap(_XGrDisplay, _XGrWindow, _XGrColormap); } pfmt = XListPixmapFormats(_XGrDisplay, &pfmt_count); if (!pfmt || pfmt_count <= 0) { XCloseDisplay(_XGrDisplay); fprintf(stderr, "X11 driver init: cannot list pixmap formats\n"); goto done; } bpp = 0; pad = 0; for (i = 0; i < pfmt_count; i++) { if (pfmt[i].depth == depth) { bpp = pfmt[i].bits_per_pixel; pad = pfmt[i].scanline_pad; break; } } XFree(pfmt); if (!bpp) { XCloseDisplay(_XGrDisplay); fprintf(stderr, "X11 driver init: cannot find pixmap format\n"); goto done; } switch (depth) { case 1: grxwinext.mode = GR_frameXWIN1; break; case 4: grxwinext.mode = GR_frameXWIN4; break; case 8: grxwinext.mode = GR_frameXWIN8; break; case 15: case 16: grxwinext.mode = GR_frameXWIN16; break; case 24: switch (bpp) { case 24: grxwinext.mode = GR_frameXWIN24; break; case 32: grxwinext.mode = (visual->red_mask & 0xff000000) ? GR_frameXWIN32H : GR_frameXWIN32L; break; } break; } _XGrBitsPerPixel = bpp; _XGrScanlinePad = pad; /* fixed size modes */ for (mp = &modes[1]; mp < &modes[itemsof(modes)-1]; mp++) { mp->present = TRUE; mp->bpp = bpp; mp->lineoffset = (mp->width * bpp) / 8; if (mp->width > _XGrMaxWidth) mp->present = FALSE; if (mp->height > _XGrMaxHeight) mp->present = FALSE; } /* this is the variable size mode */ mp->present = FALSE; mp->bpp = bpp; mp->lineoffset = 0; res = TRUE; done: GRX_RETURN( res ); } static void reset(void) { GRX_ENTER(); if (previous_error_handler) XSetErrorHandler(previous_error_handler); if (_XGrDisplay) XCloseDisplay(_XGrDisplay); _XGrDisplay = NULL; _XGrWindow = None; _XGrWMaped = 0; _GR_firstFreeColor = 0; _GR_lastFreeColor = -1; _XGrGenWSzChgEvents = FALSE; GRX_LEAVE(); } void _GrXwinEventGenExpose(int when) { if (when == _XGrGenExposeEvents || _XGrWindow == None || !_XGrBStoreInited) return; _XGrGenExposeEvents = when; if (_XGrGenExposeEvents == GR_GEN_EXPOSE_NO) _XGRActDrawable = (Drawable) _XGrBStore; else _XGRActDrawable = (Drawable) _XGrWindow; } void _GrXwinEventGenWMEnd(int when) { _XGrGenWMEndEvents = when; } GrVideoDriver _GrVideoDriverXWIN = { "xwin", /* name */ GR_XWIN, /* adapter type */ NULL, /* inherit modes from this driver */ modes, /* mode table */ itemsof(modes), /* # of modes */ detect, /* detection routine */ init, /* initialization routine */ reset, /* reset routine */ _xw_selectmode, /* special mode select routine */ GR_DRIVERF_USER_RESOLUTION /* arbitrary resolution possible */ };