// // Definition of X11 Screen interface // // Copyright 1998-2025 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this // file is missing or damaged, see the license at: // // https://www.fltk.org/COPYING.php // // Please see the following page on how to report bugs and issues: // // https://www.fltk.org/bugs.php // #include #include "Fl_X11_Screen_Driver.H" #include "Fl_X11_Window_Driver.H" #include "../../Fl_Scalable_Graphics_Driver.H" #include "../Posix/Fl_Posix_System_Driver.H" #include #include #include #include #include #include #include #include #include "../../Fl_Timeout.h" #include "../../flstring.h" #if HAVE_XINERAMA # include #endif # include # ifdef __sgi # include # else # include # endif // __sgi #ifdef DEBUG # include #endif // DEBUG extern Atom fl_NET_WORKAREA; // these are set by Fl::args() and FL_OVERRIDE any system colors: from Fl_get_system_colors.cxx extern const char *fl_fg; extern const char *fl_bg; extern const char *fl_bg2; // end of extern additions workaround #if !(USE_XFT || FLTK_USE_CAIRO) extern char *fl_get_font_xfld(int fnum, int size); #endif XIM Fl_X11_Screen_Driver::xim_im = 0; XIC Fl_X11_Screen_Driver::xim_ic = 0; int Fl_X11_Screen_Driver::fl_spotf = -1; int Fl_X11_Screen_Driver::fl_spots = -1; XRectangle Fl_X11_Screen_Driver::fl_spot; char Fl_X11_Screen_Driver::fl_is_over_the_spot = 0; Window Fl_X11_Screen_Driver::xim_win = 0; Fl_X11_Screen_Driver::Fl_X11_Screen_Driver() : Fl_Unix_Screen_Driver() { // X11 screen driver does not use a key table key_table = NULL; key_table_size = 0; } void Fl_X11_Screen_Driver::display(const char *d) { if (!d) return; // Issue #937: // setenv() is available since POSIX.1-2001 // https://pubs.opengroup.org/onlinepubs/009604499/functions/setenv.html #if HAVE_SETENV setenv("DISPLAY", d, 1); #else // HAVE_SETENV // Use putenv() for old systems (similar to FLTK 1.3) static char e[1024]; strcpy(e, "DISPLAY="); strlcat(e, d, sizeof(e)); char *c; for (c = e + 8; *c != ':'; c++) { if (!*c) { strlcat(e,":0.0",sizeof(e)); break; } } putenv(e); #endif // HAVE_SETENV } void fl_x11_use_display(Display *d) { fl_display = d; } int Fl_X11_Screen_Driver::XParseGeometry(const char* string, int* x, int* y, unsigned int* width, unsigned int* height) { return ::XParseGeometry(string, x, y, width, height); } void Fl_X11_Screen_Driver::own_colormap() { fl_open_display(); #if USE_COLORMAP switch (fl_visual->c_class) { case GrayScale : case PseudoColor : case DirectColor : break; default: return; // don't do anything for non-colormapped visuals } int i; XColor colors[16]; // Get the first 16 colors from the default colormap... for (i = 0; i < 16; i ++) colors[i].pixel = i; XQueryColors(fl_display, fl_colormap, colors, 16); // Create a new colormap... fl_colormap = XCreateColormap(fl_display, RootWindow(fl_display,fl_screen), fl_visual->visual, AllocNone); // Copy those first 16 colors to our own colormap: for (i = 0; i < 16; i ++) XAllocColor(fl_display, fl_colormap, colors + i); #endif // USE_COLORMAP } const char *Fl_X11_Screen_Driver::shortcut_add_key_name(unsigned key, char *p, char *buf, const char **eom) { const char* q; if (key == FL_Enter || key == '\r') q = "Enter"; // don't use Xlib's "Return": else if (key > 32 && key < 0x100) q = 0; else q = XKeysymToString(key); if (!q) { p += fl_utf8encode(fl_toupper(key), p); *p = 0; return buf; } if (p > buf) { strcpy(p,q); return buf; } else { if (eom) *eom = q; return q; } } static int test_visual(XVisualInfo& v, int flags) { if (v.screen != fl_screen) return 0; #if USE_COLORMAP if (!(flags & FL_INDEX)) { if (v.c_class != StaticColor && v.c_class != TrueColor) return 0; if (v.depth <= 8) return 0; // fltk will work better in colormap mode } if (flags & FL_RGB8) { if (v.depth < 24) return 0; } // for now, fltk does not like colormaps of more than 8 bits: if ((v.c_class&1) && v.depth > 8) return 0; #else // simpler if we can't use colormapped visuals at all: if (v.c_class != StaticColor && v.c_class != TrueColor) return 0; #endif return 1; } int Fl_X11_Screen_Driver::visual(int flags) { if (flags & FL_DOUBLE) return 0; open_display(); // always use default if possible: if (test_visual(*fl_visual, flags)) return 1; // get all the visuals: XVisualInfo vTemplate; int num; XVisualInfo *visualList = XGetVisualInfo(fl_display, 0, &vTemplate, &num); // find all matches, use the one with greatest depth: XVisualInfo *found = 0; int i; for (i =0; idepth < visualList[i].depth) found = &visualList[i]; } if (!found) {XFree((void*)visualList); return 0;} fl_visual = found; fl_colormap = XCreateColormap(fl_display, RootWindow(fl_display,fl_screen), fl_visual->visual, AllocNone); return 1; } static int fl_workarea_xywh[4] = { -1, -1, -1, -1 }; void Fl_X11_Screen_Driver::init_workarea() { Atom actual; unsigned long count, remaining; int format; long *xywh = 0; /* If there are several screens, the _NET_WORKAREA property does not give the work area of the main screen, but that of all screens together. Therefore, we use this property only when there is a single screen, and fall back to the main screen full area when there are several screens. */ if (Fl::screen_count() > 1 || XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen), fl_NET_WORKAREA, 0, 4, False, XA_CARDINAL, &actual, &format, &count, &remaining, (unsigned char **)&xywh) || !xywh || !xywh[2] || !xywh[3]) { fl_workarea_xywh[0] = screens[0].x_org; fl_workarea_xywh[1] = screens[0].y_org; fl_workarea_xywh[2] = screens[0].width; fl_workarea_xywh[3] = screens[0].height; } else { fl_workarea_xywh[0] = xywh[0]; fl_workarea_xywh[1] = xywh[1]; fl_workarea_xywh[2] = xywh[2]; fl_workarea_xywh[3] = xywh[3]; } if ( xywh ) { XFree(xywh); xywh = 0; } } int Fl_X11_Screen_Driver::x() { if (!fl_display) open_display(); return fl_workarea_xywh[0] #if USE_XFT || FLTK_USE_CAIRO / screens[0].scale #endif ; } int Fl_X11_Screen_Driver::y() { if (!fl_display) open_display(); return fl_workarea_xywh[1] #if USE_XFT || FLTK_USE_CAIRO / screens[0].scale #endif ; } int Fl_X11_Screen_Driver::w() { if (!fl_display) open_display(); return fl_workarea_xywh[2] #if USE_XFT || FLTK_USE_CAIRO / screens[0].scale #endif ; } int Fl_X11_Screen_Driver::h() { if (!fl_display) open_display(); return fl_workarea_xywh[3] #if USE_XFT || FLTK_USE_CAIRO / screens[0].scale #endif ; } #define USE_XRANDR (HAVE_DLSYM && HAVE_DLFCN_H) // means attempt to dynamically load libXrandr.so #if USE_XRANDR #include typedef struct { int width, height; int mwidth, mheight; } XRRScreenSize; typedef XRRScreenSize* (*XRRSizes_type)(Display *dpy, int screen, int *nsizes); #endif // USE_XRANDR void Fl_X11_Screen_Driver::init() { if (!fl_display) open_display(); int dpi_by_randr = 0; float dpih = 0.0f, dpiv = 0.0f; #if USE_XRANDR static XRRSizes_type XRRSizes_f = NULL; if (!XRRSizes_f) { XRRSizes_f = (XRRSizes_type)Fl_Posix_System_Driver::dlopen_or_dlsym("libXrandr", "XRRSizes"); } if (XRRSizes_f) { int nscreens; XRRScreenSize *ssize = XRRSizes_f(fl_display, fl_screen, &nscreens); //for (int i=0; i 0) { // Note: XRRSizes() *may* return nscreens == 0, see docs int mm = ssize[0].mwidth; dpih = mm ? ssize[0].width*25.4f/mm : 0.0f; mm = ssize[0].mheight; dpiv = mm ? ssize[0].height*25.4f/mm : 0.0f; dpi_by_randr = 1; } } #endif // USE_XRANDR int i; #if HAVE_XINERAMA if (XineramaIsActive(fl_display)) { XineramaScreenInfo *xsi = XineramaQueryScreens(fl_display, &num_screens); if (num_screens > MAX_SCREENS) num_screens = MAX_SCREENS; /* There's no way to use different DPI for different Xinerama screens. */ for (i =0; i MAX_SCREENS) num_screens = MAX_SCREENS; for (i =0; i= num_screens) n = 0; if (n == 0) { // for the main screen, these return the work area X = Fl::x(); Y = Fl::y(); W = Fl::w(); H = Fl::h(); } else { // for other screens, work area is full screen, screen_xywh(X, Y, W, H, n); } } void Fl_X11_Screen_Driver::screen_xywh(int &X, int &Y, int &W, int &H, int n) { if (num_screens < 0) init(); if ((n < 0) || (n >= num_screens)) n = 0; if (num_screens > 0) { #if USE_XFT || FLTK_USE_CAIRO float s = screens[n].scale; #else float s = 1; #endif X = screens[n].x_org / s; Y = screens[n].y_org / s; W = screens[n].width / s; H = screens[n].height / s; } } void Fl_X11_Screen_Driver::screen_dpi(float &h, float &v, int n) { if (num_screens < 0) init(); h = v = 0.0f; if (n >= 0 && n < num_screens) { h = dpi[n][0]; v = dpi[n][1]; } } // Implements fl_beep(). See documentation in src/fl_ask.cxx. void Fl_X11_Screen_Driver::beep(int type) { int vol; switch (type) { case FL_BEEP_ERROR : vol = 100; break; case FL_BEEP_DEFAULT : default : vol = 0; break; } if (!fl_display) open_display(); XBell(fl_display, vol); } void Fl_X11_Screen_Driver::flush() { if (fl_display) XFlush(fl_display); } extern void fl_fix_focus(); // in Fl.cxx void Fl_X11_Screen_Driver::grab(Fl_Window* win) { const char *p; static bool using_kde = ( p = getenv("XDG_CURRENT_DESKTOP") , (p && (strcmp(p, "KDE") == 0)) ); Fl_Window *fullscreen_win = NULL; Fl_Window *W; for (W = Fl::first_window(); W; W = Fl::next_window(W)) { if (W->fullscreen_active()) { fullscreen_win = W; break; } } if (win) { if (!Fl::grab()) { Window xid = fullscreen_win ? fl_xid(fullscreen_win) : fl_xid(Fl::first_window()); XGrabPointer(fl_display, xid, 1, ButtonPressMask|ButtonReleaseMask| ButtonMotionMask|PointerMotionMask, GrabModeAsync, GrabModeAsync, None, 0, fl_event_time); if (!using_kde) { // grabbing tends to stick with KDE (#904) XGrabKeyboard(fl_display, xid, 1, GrabModeAsync, GrabModeAsync, fl_event_time); } } Fl::grab_ = win; // FIXME: Fl::grab_ "should be private", but we need // a way to *set* the variable from the driver! } else { if (Fl::grab()) { // We must keep the grab in the non-EWMH fullscreen case if (!fullscreen_win || ewmh_supported()) { XUngrabKeyboard(fl_display, fl_event_time); } XUngrabPointer(fl_display, fl_event_time); // this flush is done in case the picked menu item goes into // an infinite loop, so we don't leave the X server locked up: XFlush(fl_display); Fl::grab_ = 0; // FIXME: Fl::grab_ "should be private", but we need // a way to *set* the variable from the driver! fl_fix_focus(); } } } // Wrapper around XParseColor... int Fl_X11_Screen_Driver::parse_color(const char* p, uchar& r, uchar& g, uchar& b) { // before w open the display, we try interpreting this ourselves // "None" will ultimately always return 0 if ( (fl_ascii_strcasecmp(p, "none") == 0) || (fl_ascii_strcasecmp(p, "#transparent") == 0) ) return 0; // if it's #rgb, we can do that ourselves if (Fl_Screen_Driver::parse_color(p, r, g, b)) return 1; // it's neither "None" nor hex, so finally open the diplay and ask X11 XColor x; if (!fl_display) open_display(); if (XParseColor(fl_display, fl_colormap, p, &x)) { r = (uchar)(x.red>>8); g = (uchar)(x.green>>8); b = (uchar)(x.blue>>8); return 1; } else return 0; } // Read colors that KDE writes to the xrdb database. // XGetDefault does not do the expected thing: it does not like // periods in either word. Therefore it cannot match class.Text.background. // However *.Text.background is matched by pretending the program is "Text". // But this will also match *.background if there is no *.Text.background // entry, requiring users to put in both (unless they want the text fields // the same color as the windows). static void set_selection_color(uchar r, uchar g, uchar b) { Fl::set_color(FL_SELECTION_COLOR,r,g,b); } static void getsyscolor(const char *key1, const char* key2, const char *arg, const char *defarg, void (*func)(uchar,uchar,uchar)) { if (!arg) { arg = XGetDefault(fl_display, key1, key2); if (!arg) arg = defarg; } XColor x; if (!XParseColor(fl_display, fl_colormap, arg, &x)) Fl::error("Unknown color: %s", arg); else func(x.red>>8, x.green>>8, x.blue>>8); } void Fl_X11_Screen_Driver::get_system_colors() { open_display(); const char* key1 = 0; if (Fl::first_window()) key1 = Fl::first_window()->xclass(); if (!key1) key1 = "fltk"; if (!bg2_set) getsyscolor("Text","background", fl_bg2, "#ffffff", Fl::background2); if (!fg_set) getsyscolor(key1, "foreground", fl_fg, "#000000", Fl::foreground); if (!bg_set) getsyscolor(key1, "background", fl_bg, "#c0c0c0", Fl::background); getsyscolor("Text", "selectBackground", 0, "#000080", set_selection_color); } const char *Fl_X11_Screen_Driver::get_system_scheme() { const char *s = 0L; if ((s = fl_getenv("FLTK_SCHEME")) == NULL) { const char* key = 0; if (Fl::first_window()) key = Fl::first_window()->xclass(); if (!key) key = "fltk"; open_display(); s = XGetDefault(fl_display, key, "scheme"); } return s; } int Fl_X11_Screen_Driver::compose(int& del) { int condition; unsigned char ascii = (unsigned char)Fl::e_text[0]; condition = (Fl::e_state & (FL_ALT | FL_META | FL_CTRL)) && !(ascii & 128) ; if (condition) { del = 0; return 0;} // this stuff is to be treated as a function key del = Fl::compose_state; Fl::compose_state = 0; // Only insert non-control characters: if ( (!Fl::compose_state) && ! (ascii & ~31 && ascii!=127)) { return 0; } return 1; } void Fl_X11_Screen_Driver::compose_reset() { Fl::compose_state = 0; if (xim_ic) XmbResetIC(xim_ic); } int Fl_X11_Screen_Driver::text_display_can_leak() const { #if USE_XFT || FLTK_USE_CAIRO return 1; #else return 0; #endif } // // 'fl_subimage_offsets()' - Calculate subimage offsets for an axis static inline int fl_subimage_offsets(int a, int aw, int b, int bw, int &obw) { int off; int ob; if (b >= a) { ob = b; off = 0; } else { ob = a; off = a - b; } bw -= off; if (ob + bw <= a + aw) { obw = bw; } else { obw = (a + aw) - ob; } return off; } // this handler will catch and ignore exceptions during XGetImage // to avoid an application crash extern "C" { static int xgetimageerrhandler(Display *display, XErrorEvent *error) { return 0; } } // When capturing window decoration, w is negative and X,Y,w and h are in pixels; // otherwise X,Y,w and h are in FLTK units. // Fl_RGB_Image *Fl_X11_Screen_Driver::read_win_rectangle(int X, int Y, int w, int h, Fl_Window *win, bool may_capture_subwins, bool *did_capture_subwins) { XImage *image; // Captured image int i, maxindex; // Looping vars int x, y; // Current X & Y in image unsigned char *line, // Array to hold image row *line_ptr; // Pointer to current line image unsigned char *pixel; // Current color value XColor colors[4096]; // Colors from the colormap... unsigned char cvals[4096][3]; // Color values from the colormap... unsigned index_mask, index_shift, red_mask, red_shift, green_mask, green_shift, blue_mask, blue_shift; // // Under X11 we have the option of the XGetImage() interface or SGI's // ReadDisplay extension which does all of the really hard work for // us... // int allow_outside = w < 0; // negative w allows negative X or Y, that is, window frame if (w < 0) w = - w; Window xid; if (win && !allow_outside) { if (win->as_double_window()) { // read from the window's offscreen buffer xid = Fl_X11_Window_Driver::driver(win)->other_xid->offscreen(); win = NULL; } else xid = fl_xid(win); } else xid = fl_window; float s = allow_outside ? 1 : Fl_Surface_Device::surface()->driver()->scale(); int Xs = Fl_Scalable_Graphics_Driver::floor(X, s); int Ys = Fl_Scalable_Graphics_Driver::floor(Y, s); int ws = Fl_Scalable_Graphics_Driver::floor(X+w, s) - Xs; int hs = Fl_Scalable_Graphics_Driver::floor(Y+h, s) - Ys; # ifdef __sgi if (XReadDisplayQueryExtension(fl_display, &i, &i)) { image = XReadDisplay(fl_display, xid, Xs, Ys, ws, hs, 0, NULL); } else # else image = 0; # endif // __sgi if (!image) { // fetch absolute coordinates int dx = 0, dy = 0, sx = 0, sy = 0, sw = 0, sh = 0; Window child_win; if (win) { XTranslateCoordinates(fl_display, xid, RootWindow(fl_display, fl_screen), Xs, Ys, &dx, &dy, &child_win); // screen dimensions int ns = Fl_Window_Driver::driver(win)->screen_num(); sx = screens[ns].x_org; sy = screens[ns].y_org; sw = screens[ns].width; sh = screens[ns].height; } #if ! HAVE_XRENDER if (win && !allow_outside && int(s) != s) { ws = (w+1) * s; // approximates what Fl_Graphics_Driver::cache_size() does hs = (h+1) * s; } #endif if (!allow_outside && win && Xs + ws > int(win->w()*s)) ws = win->w()*s - Xs; if (!allow_outside && win && Ys + hs > int(win->h()*s)) hs = win->h()*s - Ys; if (ws < 1) ws = 1; if (hs < 1) hs = 1; if (!win || (dx >= sx && dy >= sy && dx + ws <= sx+sw && dy + hs <= sy+sh) ) { // the image is fully contained, we can use the traditional method // however, if the window is obscured etc. the function will still fail. Make sure we // catch the error and continue, otherwise an exception will be thrown. XErrorHandler old_handler = XSetErrorHandler(xgetimageerrhandler); image = XGetImage(fl_display, xid, Xs, Ys, ws, hs, AllPlanes, ZPixmap); XSetErrorHandler(old_handler); } else { // image is crossing borders, determine visible region int nw, nh, noffx, noffy; noffx = fl_subimage_offsets(sx, sw, dx, ws, nw); noffy = fl_subimage_offsets(sy, sh, dy, hs, nh); if (nw <= 0 || nh <= 0) return 0; // allocate the image int bpp = fl_visual->depth + ((fl_visual->depth / 8) % 2) * 8; char* buf = (char*)malloc((bpp / 8) * ws * hs); image = XCreateImage(fl_display, fl_visual->visual, fl_visual->depth, ZPixmap, 0, buf, ws, hs, bpp, 0); if (!image) { if (buf) free(buf); return 0; } XErrorHandler old_handler = XSetErrorHandler(xgetimageerrhandler); XImage *subimg = XGetSubImage(fl_display, xid, Xs + noffx, Ys + noffy, nw, nh, AllPlanes, ZPixmap, image, noffx, noffy); XSetErrorHandler(old_handler); if (!subimg) { XDestroyImage(image); return 0; } } } if (!image) return 0; if (s != 1) { w = ws; h = hs; } #ifdef DEBUG printf("width = %d\n", image->width); printf("height = %d\n", image->height); printf("xoffset = %d\n", image->xoffset); printf("format = %d\n", image->format); printf("data = %p\n", image->data); printf("byte_order = %d\n", image->byte_order); printf("bitmap_unit = %d\n", image->bitmap_unit); printf("bitmap_bit_order = %d\n", image->bitmap_bit_order); printf("bitmap_pad = %d\n", image->bitmap_pad); printf("depth = %d\n", image->depth); printf("bytes_per_line = %d\n", image->bytes_per_line); printf("bits_per_pixel = %d\n", image->bits_per_pixel); printf("red_mask = %08x\n", image->red_mask); printf("green_mask = %08x\n", image->green_mask); printf("blue_mask = %08x\n", image->blue_mask); printf("map_entries = %d\n", fl_visual->visual->map_entries); #endif // DEBUG const int d = 3; // Depth of image uchar *p = NULL; // Allocate the image data array as needed... p = new uchar[w * h * d]; // Initialize the default colors/alpha in the whole image... memset(p, 0, w * h * d); // Check that we have valid mask/shift values... if (!image->red_mask && image->bits_per_pixel > 12) { // Greater than 12 bits must be TrueColor... image->red_mask = fl_visual->visual->red_mask; image->green_mask = fl_visual->visual->green_mask; image->blue_mask = fl_visual->visual->blue_mask; #ifdef DEBUG // Defined in Fl_Xlib_Graphics_Driver_color.cxx extern uchar fl_redmask, fl_greenmask, fl_bluemask; extern int fl_redshift, fl_greenshift, fl_blueshift; puts("\n---- UPDATED ----"); printf("fl_redmask = %08x\n", fl_redmask); printf("fl_redshift = %d\n", fl_redshift); printf("fl_greenmask = %08x\n", fl_greenmask); printf("fl_greenshift = %d\n", fl_greenshift); printf("fl_bluemask = %08x\n", fl_bluemask); printf("fl_blueshift = %d\n", fl_blueshift); printf("red_mask = %08x\n", image->red_mask); printf("green_mask = %08x\n", image->green_mask); printf("blue_mask = %08x\n", image->blue_mask); #endif // DEBUG } // Check if we have colormap image... if (!image->red_mask) { // Get the colormap entries for this window... maxindex = fl_visual->visual->map_entries; for (i = 0; i < maxindex; i ++) colors[i].pixel = i; XQueryColors(fl_display, fl_colormap, colors, maxindex); for (i = 0; i < maxindex; i ++) { cvals[i][0] = colors[i].red >> 8; cvals[i][1] = colors[i].green >> 8; cvals[i][2] = colors[i].blue >> 8; } // Read the pixels and output an RGB image... for (y = 0; y < image->height; y ++) { pixel = (unsigned char *)(image->data + y * image->bytes_per_line); line = p + y * w * d; switch (image->bits_per_pixel) { case 1 : for (x = image->width, line_ptr = line, index_mask = 128; x > 0; x --, line_ptr += d) { if (*pixel & index_mask) { line_ptr[0] = cvals[1][0]; line_ptr[1] = cvals[1][1]; line_ptr[2] = cvals[1][2]; } else { line_ptr[0] = cvals[0][0]; line_ptr[1] = cvals[0][1]; line_ptr[2] = cvals[0][2]; } if (index_mask > 1) { index_mask >>= 1; } else { index_mask = 128; pixel ++; } } break; case 2 : for (x = image->width, line_ptr = line, index_shift = 6; x > 0; x --, line_ptr += d) { i = (*pixel >> index_shift) & 3; line_ptr[0] = cvals[i][0]; line_ptr[1] = cvals[i][1]; line_ptr[2] = cvals[i][2]; if (index_shift > 0) { index_shift -= 2; } else { index_shift = 6; pixel ++; } } break; case 4 : for (x = image->width, line_ptr = line, index_shift = 4; x > 0; x --, line_ptr += d) { if (index_shift == 4) i = (*pixel >> 4) & 15; else i = *pixel & 15; line_ptr[0] = cvals[i][0]; line_ptr[1] = cvals[i][1]; line_ptr[2] = cvals[i][2]; if (index_shift > 0) { index_shift = 0; } else { index_shift = 4; pixel ++; } } break; case 8 : for (x = image->width, line_ptr = line; x > 0; x --, line_ptr += d, pixel ++) { line_ptr[0] = cvals[*pixel][0]; line_ptr[1] = cvals[*pixel][1]; line_ptr[2] = cvals[*pixel][2]; } break; case 12 : for (x = image->width, line_ptr = line, index_shift = 0; x > 0; x --, line_ptr += d) { if (index_shift == 0) { i = ((pixel[0] << 4) | (pixel[1] >> 4)) & 4095; } else { i = ((pixel[1] << 8) | pixel[2]) & 4095; } line_ptr[0] = cvals[i][0]; line_ptr[1] = cvals[i][1]; line_ptr[2] = cvals[i][2]; if (index_shift == 0) { index_shift = 4; } else { index_shift = 0; pixel += 3; } } break; } } } else { // RGB(A) image, so figure out the shifts & masks... red_mask = image->red_mask; red_shift = 0; while ((red_mask & 1) == 0) { red_mask >>= 1; red_shift ++; } green_mask = image->green_mask; green_shift = 0; while ((green_mask & 1) == 0) { green_mask >>= 1; green_shift ++; } blue_mask = image->blue_mask; blue_shift = 0; while ((blue_mask & 1) == 0) { blue_mask >>= 1; blue_shift ++; } // Read the pixels and output an RGB image... for (y = 0; y < image->height; y ++) { pixel = (unsigned char *)(image->data + y * image->bytes_per_line); line = p + y * w * d; switch (image->bits_per_pixel) { case 8 : for (x = image->width, line_ptr = line; x > 0; x --, line_ptr += d, pixel ++) { i = *pixel; line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask; line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask; line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask; } break; case 12 : for (x = image->width, line_ptr = line, index_shift = 0; x > 0; x --, line_ptr += d) { if (index_shift == 0) { i = ((pixel[0] << 4) | (pixel[1] >> 4)) & 4095; } else { i = ((pixel[1] << 8) | pixel[2]) & 4095; } line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask; line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask; line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask; if (index_shift == 0) { index_shift = 4; } else { index_shift = 0; pixel += 3; } } break; case 16 : if (image->byte_order == LSBFirst) { // Little-endian... for (x = image->width, line_ptr = line; x > 0; x --, line_ptr += d, pixel += 2) { i = (pixel[1] << 8) | pixel[0]; line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask; line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask; line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask; } } else { // Big-endian... for (x = image->width, line_ptr = line; x > 0; x --, line_ptr += d, pixel += 2) { i = (pixel[0] << 8) | pixel[1]; line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask; line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask; line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask; } } break; case 24 : if (image->byte_order == LSBFirst) { // Little-endian... for (x = image->width, line_ptr = line; x > 0; x --, line_ptr += d, pixel += 3) { i = (((pixel[2] << 8) | pixel[1]) << 8) | pixel[0]; line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask; line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask; line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask; } } else { // Big-endian... for (x = image->width, line_ptr = line; x > 0; x --, line_ptr += d, pixel += 3) { i = (((pixel[0] << 8) | pixel[1]) << 8) | pixel[2]; line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask; line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask; line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask; } } break; case 32 : if (image->byte_order == LSBFirst) { // Little-endian... for (x = image->width, line_ptr = line; x > 0; x --, line_ptr += d, pixel += 4) { i = (((((pixel[3] << 8) | pixel[2]) << 8) | pixel[1]) << 8) | pixel[0]; line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask; line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask; line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask; } } else { // Big-endian... for (x = image->width, line_ptr = line; x > 0; x --, line_ptr += d, pixel += 4) { i = (((((pixel[0] << 8) | pixel[1]) << 8) | pixel[2]) << 8) | pixel[3]; line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask; line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask; line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask; } } break; } } } // Destroy the X image we've read and return the RGB(A) image... XDestroyImage(image); Fl_RGB_Image *rgb = new Fl_RGB_Image(p, w, h, d); rgb->alloc_array = 1; return rgb; } void Fl_X11_Screen_Driver::offscreen_size(Fl_Offscreen off, int &width, int &height) { int px, py; unsigned w, h, b, d; Window root; XGetGeometry(fl_display, (Pixmap)off, &root, &px, &py, &w, &h, &b, &d); width = (int)w; height = (int)h; } void Fl_X11_Screen_Driver::reset_spot(void) { fl_spot.x = -1; fl_spot.y = -1; //if (xim_ic) XUnsetICFocus(xim_ic); } void Fl_X11_Screen_Driver::set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win) { int change = 0; XVaNestedList preedit_attr; static XFontSet fs = NULL; char **missing_list = NULL; int missing_count = 0; char *def_string = NULL; char *fnt = NULL; bool must_free_fnt = true; static XIC ic = NULL; if (!xim_ic || !fl_is_over_the_spot) return; if (Fl::focus()) { // handle case when text widget is inside subwindow Fl_Window *focuswin = Fl::focus()->window(); while (focuswin && focuswin->parent()) { X += focuswin->x(); Y += focuswin->y(); focuswin = focuswin->window(); } } // XSetICFocus(xim_ic); if (X != fl_spot.x || Y != fl_spot.y) { fl_spot.x = X; fl_spot.y = Y; fl_spot.height = H; fl_spot.width = W; change = 1; } if (font != fl_spotf || size != fl_spots) { fl_spotf = font; fl_spots = size; change = 1; if (fs) { XFreeFontSet(fl_display, fs); } #if USE_XFT || FLTK_USE_CAIRO fnt = NULL; // FIXME: missing XFT support here #else fnt = fl_get_font_xfld(font, size); #endif if (!fnt) { fnt = (char*)"-misc-fixed-*"; must_free_fnt = false; } fs = XCreateFontSet(fl_display, fnt, &missing_list, &missing_count, &def_string); if (missing_list) XFreeStringList(missing_list); } if (xim_ic != ic) { ic = xim_ic; change = 1; } if (fnt && must_free_fnt) free(fnt); if (!change) return; float s = Fl_Graphics_Driver::default_driver().scale(); XRectangle fl_spot_unscaled = { short(fl_spot.x * s), short(fl_spot.y * s), (unsigned short)(fl_spot.width * s), (unsigned short)(fl_spot.height * s) }; preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &fl_spot_unscaled, XNFontSet, fs, NULL); XSetICValues(xim_ic, XNPreeditAttributes, preedit_attr, NULL); XFree(preedit_attr); } #if USE_XFT || FLTK_USE_CAIRO //NOTICE: returns -1 if x,y is not in any screen int Fl_X11_Screen_Driver::screen_num_unscaled(int x, int y) { int screen = -1; if (num_screens < 0) init(); int i; for (i = 0; i < num_screens; i ++) { int sx = screens[i].x_org, sy = screens[i].y_org, sw = screens[i].width, sh = screens[i].height; if ((x >= sx) && (x < (sx+sw)) && (y >= sy) && (y < (sy+sh))) { screen = i; break; } } return screen; } // set the desktop's default scaling value void Fl_X11_Screen_Driver::desktop_scale_factor() { if (this->current_xft_dpi == 0.) { // Try getting the Xft.dpi resource value char *s = XGetDefault(fl_display, "Xft", "dpi"); if (s && sscanf(s, "%f", &(this->current_xft_dpi)) == 1) { float factor = this->current_xft_dpi / 96.; // checks to prevent potential crash (factor <= 0) or very large factors // and round nearly 1 or nearly 2 values (issue #1138) if (factor < 1.1) factor = 1; else if (factor > 1.8 && factor < 2.2) factor = 2; else if (factor > 10.0) factor = 10.0; int i; for (i = 0; i < screen_count(); i++) scale(i, factor); } } } #endif // USE_XFT || FLTK_USE_CAIRO