/* x.c * (c) 2002 Petr 'Brain' Kulhavy * This file is a part of the Links program, released under GPL. */ /* Takovej mensi problemek se scrollovanim: * * Mikulas a Xy zpusobili, ze scrollovani a prekreslovani je asynchronni. To znamena, ze * je v tom peknej bordylek. Kdyz BFU scrollne s oknem, tak se zavola funkce scroll. Ta * posle Xum XCopyArea (prekopiruje prislusny kus okna) a vygeneruje eventu * (GraphicsExpose) na postizenou (odkrytou) oblast. Funkce XCopyArea pripadne vygeneruje * dalsi GraphicsExpose eventu na postizenou oblast, ktera se muze objevit, kdyz je * linksove okno prekryto jinym oknem. * * Funkce scroll skonci. V event handleru se nekdy v budoucnosti (treba za tyden) * zpracuji eventy od Xu, mezi nimi i GraphicsExpose - tedy prekreslovani postizenych * oblasti. * * Problem je v tom, ze v okamziku, kdy scroll skonci, neni obrazovka prekreslena do * konzistentniho stavu (misty je garbaz) a navic se muze volat dalsi scroll. Tedy * XCopyArea muze posunout garbaz nekam do cudu a az se dostane na radu prekreslovani * postizenych oblasti, garbaz se uz nikdy neprekresli. * * Ja jsem navrhoval udelat scrollovani synchronni, to znamena, ze v okamziku, kdy scroll * skonci, bude okno v konzistentnim stavu. To by znamenalo volat ze scrollu zpracovavani * eventu (alespon GraphicsExpose). To by ovsem nepomohlo, protoze prekreslovaci funkce * neprekresluje, ale registruje si bottom halfy a podobny ptakoviny a prekresluje az * nekdy v budoucnosti. Navic Mikulas rikal, ze prekreslovaci funkce muze generovat dalsi * prekreslovani (sice jsem nepochopil jak, ale hlavne, ze je vecirek), takze by to * neslo. * * Proto Mikulas vymyslel genialni tah - takzvany "genitah". Ve funkci scroll se projede * fronta eventu od Xserveru a vyberou se GraphicsExp(l)ose eventy a ulozi se do zvlastni * fronty. Ve funkci na zpracovani Xovych eventu se zpracuji eventy z teto fronty. Na * zacatku scrollovaci funkce se projedou vsechny eventy ve zvlastni fronte a updatuji se * jim souradnice podle prislusneho scrollu. * * Na to jsem ja vymyslel uzasnou vymluvu: co kdyz 1. scroll vyrobi nejake postizene * oblasti a 2. scroll bude mit jinou clipovaci oblast, ktera bude tu postizenou oblast * zasahovat z casti. Tak to se bude jako ta postizena oblast stipat na casti a ty casti * se posunou podle toho, jestli jsou zasazene tim 2. scrollem? Tim jsem ho utrel, jak * zpoceny celo. * * Takze se to nakonec udela tak, ze ze scrollu vratim hromadu rectanglu, ktere se maji * prekreslit, a Mikulas si s tim udela, co bude chtit. Podobne jako ve svgalib, kde se * vrati 1 a Mikulas si prislusnou odkrytou oblast prekresli sam. Doufam jen, ze to je * posledni verze a ze nevzniknou dalsi problemy. * * Ve funkci scroll tedy pribude argument struct rect_set **. */ /* Data od XImage se alokujou pomoci malloc. get_links_icon musi alokovat taky * pomoci malloc. */ /* Pozor: po kazdem XSync nebo XFlush se musi dat * X_SCHEDULE_PROCESS_EVENTS * jinak to bude cekat na filedescriptoru, i kdyz to ma eventy uz ve fronte. * -- mikulas */ #include "cfg.h" #ifdef GRDRV_X /* #define X_DEBUG */ /* #define SC_DEBUG */ #if defined(X_DEBUG) || defined(SC_DEBUG) #define MESSAGE(a) fprintf(stderr,"%s",a); #endif #include "links.h" /* Mikulas je PRASE: definuje makro "format" a navrch to jeste nechce vopravit */ #ifdef format #undef format #endif #if defined(HAVE_XOPENIM) && defined(HAVE_XCLOSEIM) && defined(HAVE_XCREATEIC) && defined(HAVE_XDESTROYIC) && (defined(HAVE_XWCLOOKUPSTRING) || defined(HAVE_XUTF8LOOKUPSTRING)) #define X_INPUT_METHOD #endif typedef void *void_p64; #ifdef OPENVMS_64BIT #pragma __pointer_size 32 #endif #include #include #include #include #include #if defined(HAVE_X11_XLOCALE_H) && defined(HAVE_XSETLOCALE) #ifdef OPENVMS #undef LC_ALL #undef LC_COLLATE #undef LC_CTYPE #undef LC_MONETARY #undef LC_NUMERIC #undef LC_TIME #endif #include #else #ifdef HAVE_SETLOCALE #undef HAVE_SETLOCALE #endif #endif #ifndef XK_MISCELLANY #define XK_MISCELLANY #endif #ifndef XK_LATIN1 #define XK_LATIN1 #endif #include static void *x_malloc(size_t len, int mayfail) { void *p; if (len > MAX_SIZE_T) { if (mayfail) return NULL; overalloc(); } do #ifdef OPENVMS_64BIT p = _malloc32(len); #else p = malloc(len); #endif while (!p && out_of_memory(0, !mayfail ? cast_uchar "x_malloc" : NULL, 0)); return p; } static void *x_dup(void_p64 src, size_t len) { void *r = x_malloc(len, 0); return memcpy(r, src, len); } typedef unsigned char *unsigned_char_p; typedef char *char_p; typedef void *void_p; typedef XImage *XImage_p; typedef XPixmapFormatValues *XPixmapFormatValues_p; typedef XEvent *XEvent_p; typedef XKeyEvent *XKeyEvent_p; typedef XColor *XColor_p; #ifdef OPENVMS_64BIT #pragma __pointer_size 64 #endif #ifdef HAVE_LANGINFO_H #include #endif #define X_HASH_TABLE_SIZE 64 #define X_MAX_CLIPBOARD_SIZE (15*1024*1024) #define SELECTION_NOTIFY_TIMEOUT 10000 static int x_default_window_width; static int x_default_window_height; static long (*x_get_color_function)(int); static void x_translate_colors(unsigned char *data, int x, int y, ssize_t skip); static void x_convert_to_216(unsigned char *data, int x, int y, ssize_t skip); static void selection_request(XEvent *event); #ifdef OPENVMS_64BIT #pragma __pointer_size 32 #endif static Display *x_display = NULL; /* display */ static Visual *x_default_visual; static XVisualInfo vinfo; #ifdef OPENVMS_64BIT #pragma __pointer_size 64 #endif static int x_fd = -1; /* x socket */ static unsigned char *x_display_string = NULL; static int x_screen; /* screen */ static int x_display_height, x_display_width; /* screen dimensions */ static unsigned long x_black_pixel; /* black pixel */ static int x_depth, x_bitmap_bpp; /* bits per pixel and bytes per pixel */ static int x_bitmap_scanline_pad; /* bitmap scanline_padding in bytes */ static int x_input_encoding; /* locales encoding */ static int x_bitmap_bit_order; static XColor_p static_color_map = NULL; static unsigned char x_have_palette; static unsigned char x_use_static_color_table; static unsigned char *static_color_table = NULL; struct { unsigned char failure; unsigned char extra_allocated; unsigned short alloc_count; } *static_color_struct = NULL; #define X_STATIC_COLORS (x_depth < 8 ? 8 : 216) #define X_STATIC_CODE (x_depth < 8 ? 801 : 833) static unsigned short *color_map_16bit = NULL; static unsigned *color_map_24bit = NULL; static Window x_root_window, fake_window; static int fake_window_initialized = 0; static GC x_normal_gc = 0, x_copy_gc = 0, x_drawbitmap_gc = 0, x_scroll_gc = 0; static long x_normal_gc_color; static struct rect x_scroll_gc_rect; static Colormap x_default_colormap, x_colormap; static Atom x_delete_window_atom, x_wm_protocols_atom, x_sel_atom, x_targets_atom, x_utf8_string_atom; static Pixmap x_icon = 0; #ifdef X_INPUT_METHOD static XIM xim = NULL; #endif extern struct graphics_driver x_driver; static unsigned char *x_driver_param = NULL; static int n_wins = 0; /* number of windows */ static struct list_head bitmaps = { &bitmaps, &bitmaps }; #define XPIXMAPP(a) ((struct x_pixmapa *)(a)) #define X_TYPE_NOTHING 0 #define X_TYPE_PIXMAP 1 #define X_TYPE_IMAGE 2 struct x_pixmapa { unsigned char type; union { XImage_p image; Pixmap pixmap; } data; list_entry_1st list_entry_last }; static struct { unsigned char count; struct graphics_device **pointer; } x_hash_table[X_HASH_TABLE_SIZE]; /* string in clipboard is in UTF-8 */ static unsigned char *x_my_clipboard = NULL; struct window_info { #ifdef X_INPUT_METHOD XIC xic; #endif Window window; }; static inline struct window_info *get_window_info(struct graphics_device *dev) { return dev->driver_data; } /*----------------------------------------------------------------------*/ /* Tyhle opicarny tu jsou pro zvyseni rychlosti. Flush se nedela pri kazde operaci, ale * rekne se, ze je potreba udelat flush, a zaregistruje se bottom-half, ktery flush * provede. Takze jakmile se vrati rizeni do select smycky, tak se provede flush. */ static int x_wait_for_event(int sec) { return can_read_timeout(x_fd, sec); } static void x_process_events(void *data); static unsigned char flush_in_progress = 0; static unsigned char process_events_in_progress = 0; static inline void X_SCHEDULE_PROCESS_EVENTS(void) { if (!process_events_in_progress) { register_bottom_half(x_process_events, NULL); process_events_in_progress = 1; } } static void x_do_flush(void *ignore) { flush_in_progress = 0; XFlush(x_display); X_SCHEDULE_PROCESS_EVENTS(); } static inline void X_FLUSH(void) { #ifdef INTERIX /* * Interix has some bug, it locks up in _XWaitForWritable. * As a workaround, do synchronous flushes. */ x_do_flush(NULL); #else if (!flush_in_progress) { register_bottom_half(x_do_flush, NULL); flush_in_progress = 1; } #endif } static void x_pixmap_failed(void *ignore); #ifdef OPENVMS_64BIT #pragma __pointer_size 32 #endif static int (*original_error_handler)(Display *, XErrorEvent *) = NULL; static unsigned char pixmap_mode = 0; static int pixmap_handler(Display *d, XErrorEvent *e) { if (pixmap_mode == 2) { if (e->request_code == X_CreatePixmap) { pixmap_mode = 1; register_bottom_half(x_pixmap_failed, NULL); return 0; } } if (pixmap_mode == 1) { if (e->request_code == X_CreatePixmap || e->request_code == X_PutImage || e->request_code == X_CopyArea || e->request_code == X_FreePixmap) { return 0; } } return original_error_handler(d, e); } #ifdef OPENVMS_64BIT #pragma __pointer_size 64 #endif static void x_pixmap_failed(void *no_reinit) { struct x_pixmapa *p; struct list_head *lp; foreach(struct x_pixmapa, p, lp, bitmaps) { if (p->type == X_TYPE_PIXMAP) { XFreePixmap(x_display, p->data.pixmap); p->type = X_TYPE_NOTHING; } } XSync(x_display, False); X_SCHEDULE_PROCESS_EVENTS(); XSetErrorHandler(original_error_handler); original_error_handler = NULL; pixmap_mode = 0; if (!no_reinit) flush_bitmaps(1, 1, 1); } static void setup_pixmap_handler(void) { original_error_handler = XSetErrorHandler(pixmap_handler); pixmap_mode = 2; } static void undo_pixmap_handler(void) { if (pixmap_mode == 2) { XSetErrorHandler(original_error_handler); original_error_handler = NULL; pixmap_mode = 0; } if (pixmap_mode == 1) { x_pixmap_failed(DUMMY); } unregister_bottom_half(x_pixmap_failed, NULL); } #ifdef OPENVMS_64BIT #pragma __pointer_size 32 #endif static int (*old_error_handler)(Display *, XErrorEvent *) = NULL; static unsigned char failure_happened; static unsigned char test_request_code; static int failure_handler(Display *d, XErrorEvent *e) { if (e->request_code != test_request_code) return old_error_handler(d, e); failure_happened = 1; return 0; } #ifdef OPENVMS_64BIT #pragma __pointer_size 64 #endif static void x_prepare_for_failure(unsigned char code) { if (old_error_handler) internal_error("x_prepare_for_failure: double call"); failure_happened = 0; test_request_code = code; old_error_handler = XSetErrorHandler(failure_handler); } static int x_test_for_failure(void) { XSync(x_display, False); X_SCHEDULE_PROCESS_EVENTS(); XSetErrorHandler(old_error_handler); old_error_handler = NULL; return failure_happened; } /* suppose l h) *n = h; if (*n < l) *n = l; } static const unsigned char alloc_sequence_216[216] = { 0, 215, 30, 185, 5, 35, 180, 210, 86, 203, 23, 98, 192, 119, 126, 43, 173, 12, 74, 140, 188, 161, 27, 54, 95, 204, 17, 114, 198, 11, 107, 113, 60, 65, 68, 81, 102, 108, 120, 132, 146, 164, 20, 89, 92, 158, 170, 191, 6, 37, 40, 46, 49, 52, 57, 123, 129, 135, 151, 154, 175, 178, 195, 207, 24, 71, 77, 78, 101, 143, 149, 167, 2, 8, 14, 18, 29, 32, 59, 62, 66, 72, 83, 84, 90, 96, 104, 110, 116, 125, 131, 137, 138, 144, 156, 162, 168, 182, 186, 197, 200, 209, 212, 1, 3, 4, 7, 9, 10, 13, 15, 16, 19, 21, 22, 25, 26, 28, 31, 33, 34, 36, 38, 39, 41, 42, 44, 45, 47, 48, 50, 51, 53, 55, 56, 58, 61, 63, 64, 67, 69, 70, 73, 75, 76, 79, 80, 82, 85, 87, 88, 91, 93, 94, 97, 99, 100, 103, 105, 106, 109, 111, 112, 115, 117, 118, 121, 122, 124, 127, 128, 130, 133, 134, 136, 139, 141, 142, 145, 147, 148, 150, 152, 153, 155, 157, 159, 160, 163, 165, 166, 169, 171, 172, 174, 176, 177, 179, 181, 183, 184, 187, 189, 190, 193, 194, 196, 199, 201, 202, 205, 206, 208, 211, 213, 214 }; static void x_fill_color_table(XColor colors[256], unsigned q) { unsigned a; unsigned limit = 1U << x_depth; for (a = 0; a < limit; a++) { unsigned rgb[3]; q_palette(q, a, 65535, rgb); colors[a].red = rgb[0]; colors[a].green = rgb[1]; colors[a].blue = rgb[2]; colors[a].pixel = a; colors[a].flags = DoRed | DoGreen | DoBlue; } } static double rgb_distance_with_border(int r1, int g1, int b1, int r2, int g2, int b2) { double distance = rgb_distance(r1, g1, b1, r2, g2, b2); if (!r1) distance += r2 * 4294967296.; if (!g1) distance += g2 * 4294967296.; if (!b1) distance += b2 * 4294967296.; if (r1 == 0xffff) distance += (0xffff - r2) * 4294967296.; if (g1 == 0xffff) distance += (0xffff - g2) * 4294967296.; if (b1 == 0xffff) distance += (0xffff - b2) * 4294967296.; return distance; } static int get_nearest_color(unsigned rgb[3]) { int j; double best_distance = 0; int best = -1; for (j = 0; j < 1 << x_depth; j++) { double distance; if (static_color_struct[j].failure) continue; distance = rgb_distance_with_border(rgb[0], rgb[1], rgb[2], static_color_map[j].red, static_color_map[j].green, static_color_map[j].blue); if (best < 0 || distance < best_distance) { best = j; best_distance = distance; } } return best; } static int swap_color(int c, XColor_p xc, int test_count, double *distance, unsigned rgb[3]) { unsigned long old_px, new_px; double new_distance; /* * The manual page on XAllocColor says that it returns the closest * color. * In reality, it fails if it can't allocate the color. * * In case there were some other implementations that return the * closest color, we check the distance after allocation and we fail if * the distance would be increased. */ if (!XAllocColor(x_display, x_default_colormap, xc)) { return -1; } new_px = xc->pixel; new_distance = rgb_distance(xc->red, xc->green, xc->blue, rgb[0], rgb[1], rgb[2]); if (new_px >= 256 || (test_count && (static_color_struct[new_px].alloc_count || static_color_struct[new_px].extra_allocated)) || new_distance > *distance) { XFreeColors(x_display, x_default_colormap, &xc->pixel, 1, 0); return -1; } old_px = static_color_table[c]; static_color_struct[old_px].alloc_count--; XFreeColors(x_display, x_default_colormap, &old_px, 1, 0); static_color_map[new_px] = *xc; static_color_struct[new_px].alloc_count++; static_color_struct[new_px].failure = 0; static_color_struct[new_px].extra_allocated = 1; static_color_table[c] = (unsigned char)new_px; *distance = new_distance; return 0; } static unsigned char *x_query_palette(void) { unsigned rgb[3]; int i; int some_valid; int do_alloc = vinfo.class == PseudoColor; retry: if ((int)sizeof(XColor) > MAXINT >> x_depth) overalloc(); if ((int)sizeof(*static_color_struct) > MAXINT >> x_depth) overalloc(); if (!static_color_map) static_color_map = x_malloc(sizeof(XColor) << x_depth, 0); if (!static_color_struct) static_color_struct = mem_alloc(sizeof(*static_color_struct) << x_depth); some_valid = 0; memset(static_color_map, 0, sizeof(XColor) << x_depth); memset(static_color_struct, 0, sizeof(*static_color_struct) << x_depth); for (i = 0; i < 1 << x_depth; i++) static_color_map[i].pixel = i; x_prepare_for_failure(X_QueryColors); XQueryColors(x_display, x_default_colormap, static_color_map, 1 << x_depth); if (!x_test_for_failure()) { for (i = 0; i < 1 << x_depth; i++) { if ((static_color_map[i].flags & (DoRed | DoGreen | DoBlue)) == (DoRed | DoGreen | DoBlue)) some_valid = 1; else static_color_struct[i].failure = 1; } } if (!some_valid) { memset(static_color_map, 0, sizeof(XColor) << x_depth); memset(static_color_struct, 0, sizeof(*static_color_struct) << x_depth); for (i = 0; i < 1 << x_depth; i++) { static_color_map[i].pixel = i; x_prepare_for_failure(X_QueryColors); XQueryColor(x_display, x_default_colormap, &static_color_map[i]); if (!x_test_for_failure() && (static_color_map[i].flags & (DoRed | DoGreen | DoBlue)) == (DoRed | DoGreen | DoBlue)) { some_valid = 1; } else { static_color_struct[i].failure = 1; } } } if (!some_valid) { if (vinfo.class == StaticColor) { return stracpy(cast_uchar "Could not query static colormap.\n"); } else { x_fill_color_table(static_color_map, X_STATIC_COLORS); do_alloc = 0; } } if (!static_color_table) static_color_table = mem_alloc(256); memset(static_color_table, 0, 256); for (i = 0; i < X_STATIC_COLORS; i++) { int idx = X_STATIC_COLORS == 216 ? alloc_sequence_216[i] : i; int c; q_palette(X_STATIC_COLORS, idx, 65535, rgb); another_nearest: c = get_nearest_color(rgb); if (c < 0) { do_alloc = 0; goto retry; } if (do_alloc) { XColor xc; memset(&xc, 0, sizeof xc); xc.red = static_color_map[c].red; xc.green = static_color_map[c].green; xc.blue = static_color_map[c].blue; xc.flags = DoRed | DoGreen | DoBlue; if (!XAllocColor(x_display, x_default_colormap, &xc)) { if (0) { allocated_invalid: XFreeColors(x_display, x_default_colormap, &xc.pixel, 1, 0); } static_color_struct[c].failure = 1; goto another_nearest; } else { if (xc.pixel >= 256) goto allocated_invalid; c = (unsigned char)xc.pixel; static_color_map[c] = xc; static_color_struct[c].alloc_count++; } } static_color_table[idx] = (unsigned char)c; } if (do_alloc) { double distances[256]; double max_dist; int c; for (i = 0; i < X_STATIC_COLORS; i++) { unsigned char q = static_color_table[i]; q_palette(X_STATIC_COLORS, i, 65535, rgb); distances[i] = rgb_distance(static_color_map[q].red, static_color_map[q].green, static_color_map[q].blue, rgb[0], rgb[1], rgb[2]); } try_alloc_another: max_dist = 0; c = -1; for (i = 0; i < X_STATIC_COLORS; i++) { int idx = X_STATIC_COLORS == 216 ? alloc_sequence_216[i] : i; if (distances[idx] > max_dist) { max_dist = distances[idx]; c = idx; } } if (c >= 0) { XColor xc; memset(&xc, 0, sizeof xc); q_palette(X_STATIC_COLORS, c, 65535, rgb); xc.red = rgb[0]; xc.green = rgb[1]; xc.blue = rgb[2]; xc.flags = DoRed | DoGreen | DoBlue; if (swap_color(c, &xc, 1, &distances[c], rgb)) { /*fprintf(stderr, "no more\n");*/ goto no_more_alloc; } /*fprintf(stderr, "allocated %d -> %d (%f)\n", c, (int)xc.pixel, distances[c]);*/ for (i = 0; i < X_STATIC_COLORS; i++) { double d1, d2; unsigned char cidx = static_color_table[i]; q_palette(X_STATIC_COLORS, i, 65535, rgb); d1 = rgb_distance_with_border(rgb[0], rgb[1], rgb[2], static_color_map[cidx].red, static_color_map[cidx].green, static_color_map[cidx].blue); d2 = rgb_distance_with_border(rgb[0], rgb[1], rgb[2], xc.red, xc.green, xc.blue); if (d2 < d1) { /*double old_d = distances[i];*/ if (distances[i] < rgb_distance(xc.red, xc.green, xc.blue, rgb[0], rgb[1], rgb[2])) continue; if (swap_color(i, &xc, 0, &distances[i], rgb)) { /*fprintf(stderr, "no more 2\n");*/ goto no_more_alloc; } /*fprintf(stderr, "extra swap: %d -> %d (%f -> %f)\n", i, (int)xc.pixel, old_d, distances[i]);*/ } } goto try_alloc_another; } } no_more_alloc: return NULL; } static void x_free_colors(void) { unsigned long pixels[256]; int n_pixels = 0; int i; if (!static_color_struct) return; for (i = 0; i < 1 << x_depth; i++) { while (static_color_struct[i].alloc_count) { static_color_struct[i].alloc_count--; pixels[n_pixels++] = i; } } if (n_pixels) { XFreeColors(x_display, x_default_colormap, pixels, n_pixels, 0); } } static void x_set_palette(void) { unsigned limit = 1U << x_depth; x_free_colors(); if (x_use_static_color_table) { unsigned char *m; m = x_query_palette(); if (m) mem_free(m); /* shut up coverity warning */ XStoreColors(x_display, x_colormap, static_color_map, (int)limit); } else { XColor colors[256]; x_fill_color_table(colors, limit); XStoreColors(x_display, x_colormap, colors, (int)limit); } X_FLUSH(); } static void mask_to_bitfield(unsigned long mask, int bitfield[8]) { int i; int bit = 0; for (i = sizeof(unsigned long) * 8 - 1; i >= 0; i--) { if (mask & 1UL << i) { /*fprintf(stderr, "mask %lx, bitfield[%d] = %d\n", mask, bit, i);*/ bitfield[bit++] = i; if (bit >= 8) return; } } while (bit < 8) bitfield[bit++] = -1; } static unsigned long apply_bitfield(int bitfield[8], int bits, unsigned value) { unsigned long result = 0; int bf_index = 0; while (bits-- && bf_index < 8) { if (value & 1 << bits) { if (bitfield[bf_index] >= 0) { result |= 1UL << bitfield[bf_index]; } } bf_index++; } return result; } static int create_16bit_mapping(unsigned long red_mask, unsigned long green_mask, unsigned long blue_mask) { int i; int red_bitfield[8]; int green_bitfield[8]; int blue_bitfield[8]; if ((red_mask | green_mask | blue_mask) >= 0x10000) return 0; if (!red_mask || !green_mask || !blue_mask) return 0; mask_to_bitfield(red_mask, red_bitfield); mask_to_bitfield(green_mask, green_bitfield); mask_to_bitfield(blue_mask, blue_bitfield); color_map_16bit = mem_calloc(256 * 2 * sizeof(unsigned short)); for (i = 0; i < 256; i++) { unsigned g, b; unsigned short ag, ab, result; g = i >> 5; b = i & 31; ag = (unsigned short)apply_bitfield(green_bitfield, x_depth - 10, g); ab = (unsigned short)apply_bitfield(blue_bitfield, 5, b); result = ag | ab; color_map_16bit[i + (x_bitmap_bit_order == MSBFirst) * 256] = (unsigned short)result; } for (i = 0; i < (x_depth == 15 ? 128 : 256); i++) { unsigned r, g; unsigned short ar, ag, result; r = i >> (x_depth - 13); g = (i << (x_depth - 13)) & ((1 << (x_depth - 10)) - 1); ar = (unsigned short)apply_bitfield(red_bitfield, 5, r); ag = (unsigned short)apply_bitfield(green_bitfield, x_depth - 10, g); result = ar | ag; color_map_16bit[i + (x_bitmap_bit_order == LSBFirst) * 256] = (unsigned short)result; } return 1; } static void create_24bit_component_mapping(unsigned long mask, unsigned *ptr) { int i; int bitfield[8]; mask_to_bitfield(mask, bitfield); for (i = 0; i < 256; i++) { unsigned result = (unsigned)apply_bitfield(bitfield, 8, i); if (x_bitmap_bit_order == MSBFirst && x_bitmap_bpp == 4) { result = (result >> 24) | ((result & 0xff0000) >> 8) | ((result & 0xff00) << 8) | ((result & 0xff) << 24); } ptr[i] = result; } } static int create_24bit_mapping(unsigned long red_mask, unsigned long green_mask, unsigned long blue_mask) { if ((red_mask | green_mask | blue_mask) > 0xFFFFFFFFUL) return 0; if (!red_mask || !green_mask || !blue_mask) return 0; color_map_24bit = mem_alloc(256 * 3 * sizeof(unsigned)); create_24bit_component_mapping(blue_mask, color_map_24bit); create_24bit_component_mapping(green_mask, color_map_24bit + 256); create_24bit_component_mapping(red_mask, color_map_24bit + 256 * 2); return 1; } static inline int trans_key(unsigned char *str, int table) { if (table==utf8_table){int a; GET_UTF_8(str,a);return a;} if (*str<128)return *str; return cp2u(*str,table); } /* translates X keys to links representation */ /* return value: 1=valid key, 0=nothing */ static int x_translate_key(struct graphics_device *dev, XKeyEvent_p e, int *key, int *flag) { KeySym ks = 0; static XComposeStatus comp = { NULL, 0 }; static char str[16]; #define str_size ((int)(sizeof(str) - 1)) int table = x_input_encoding < 0 ? g_kbd_codepage(&x_driver) : x_input_encoding; int len; #ifdef X_INPUT_METHOD if (get_window_info(dev)->xic) { Status status; #ifndef HAVE_XUTF8LOOKUPSTRING { wchar_t wc; len = XwcLookupString(get_window_info(dev)->xic, e, &wc, 1, &ks, &status); if (len == 1) { strcpy(str, cast_const_char encode_utf_8(wc)); len = strlen(str); } else len = 0; } #else { len = Xutf8LookupString(get_window_info(dev)->xic, e, str, str_size, &ks, &status); } #endif table = utf8_table; /*fprintf(stderr, "len: %d, ks %ld, status %d\n", len, ks, status);*/ } else #endif { len = XLookupString(e, str, str_size, &ks, &comp); } str[len > str_size ? str_size : len] = 0; if (!len) str[0] = (char)ks, str[1] = 0; *flag = 0; *key = 0; /* alt, control, shift ... */ if (e->state & ShiftMask) *flag |= KBD_SHIFT; if (e->state & ControlMask) *flag |= KBD_CTRL; if (e->state & Mod1Mask) *flag |= KBD_ALT; /* alt-f4 */ if (*flag & KBD_ALT && ks == XK_F4) { *key = KBD_CTRL_C; *flag = 0; return 1; } /* ctrl-c */ if (*flag & KBD_CTRL && (ks == XK_c || ks == XK_C)) {*key = KBD_CTRL_C; *flag = 0; return 1; } if (ks == NoSymbol) { return 0; } else if (ks == XK_Return) { *key = KBD_ENTER; } else if (ks == XK_BackSpace) { *key = KBD_BS; } else if (ks == XK_Tab #ifdef XK_KP_Tab || ks == XK_KP_Tab #endif ) { *key = KBD_TAB; } else if (ks == XK_Escape) { *key = KBD_ESC; } else if (ks == XK_Left #ifdef XK_KP_Left || ks == XK_KP_Left #endif ) { *key = KBD_LEFT; } else if (ks == XK_Right #ifdef XK_KP_Right || ks == XK_KP_Right #endif ) { *key = KBD_RIGHT; } else if (ks == XK_Up #ifdef XK_KP_Up || ks == XK_KP_Up #endif ) { *key = KBD_UP; } else if (ks == XK_Down #ifdef XK_KP_Down || ks == XK_KP_Down #endif ) { *key = KBD_DOWN; } else if (ks == XK_Insert #ifdef XK_KP_Insert || ks == XK_KP_Insert #endif ) { *key = KBD_INS; #ifdef OPENVMS } else if (ks == XK_Delete) { *key = KBD_BS; #endif } else if (ks == XK_Delete #ifdef XK_KP_Delete || ks == XK_KP_Delete #endif ) { *key = KBD_DEL; } else if (ks == XK_Home #ifdef XK_KP_Home || ks == XK_KP_Home #endif ) { *key = KBD_HOME; } else if (ks == XK_End #ifdef XK_KP_End || ks == XK_KP_End #endif ) { *key = KBD_END; } else if (0 #ifdef XK_KP_Page_Up || ks == XK_KP_Page_Up #endif #ifdef XK_Page_Up || ks == XK_Page_Up #endif ) { *key = KBD_PAGE_UP; } else if (0 #ifdef XK_KP_Page_Down || ks == XK_KP_Page_Down #endif #ifdef XK_Page_Down || ks == XK_Page_Down #endif ) { *key = KBD_PAGE_DOWN; } else if (ks == XK_F1 #ifdef XK_KP_F1 || ks == XK_KP_F1 #endif ) { *key = KBD_F1; } else if (ks == XK_F2 #ifdef XK_KP_F2 || ks == XK_KP_F2 #endif ) { *key = KBD_F2; } else if (ks == XK_F3 #ifdef XK_KP_F3 || ks == XK_KP_F3 #endif ) { *key = KBD_F3; } else if (ks == XK_F4 #ifdef XK_KP_F4 || ks == XK_KP_F4 #endif ) { *key = KBD_F4; } else if (ks == XK_F5) { *key = KBD_F5; } else if (ks == XK_F6) { *key = KBD_F6; } else if (ks == XK_F7) { *key = KBD_F7; } else if (ks == XK_F8) { *key = KBD_F8; } else if (ks == XK_F9) { *key = KBD_F9; } else if (ks == XK_F10) { *key = KBD_F10; #ifdef OPENVMS } else if (ks == XK_F11) { *key = KBD_ESC; #else } else if (ks == XK_F11) { *key = KBD_F11; #endif } else if (ks == XK_F12) { *key = KBD_F12; } else if (ks == XK_KP_Subtract) { *key = '-'; } else if (ks == XK_KP_Decimal) { *key = '.'; } else if (ks == XK_KP_Divide) { *key = '/'; } else if (ks == XK_KP_Space) { *key = ' '; } else if (ks == XK_KP_Enter) { *key = KBD_ENTER; } else if (ks == XK_KP_Equal) { *key = '='; } else if (ks == XK_KP_Multiply) { *key = '*'; } else if (ks == XK_KP_Add) { *key = '+'; } else if (ks == XK_KP_0) { *key = '0'; } else if (ks == XK_KP_1) { *key = '1'; } else if (ks == XK_KP_2) { *key = '2'; } else if (ks == XK_KP_3) { *key = '3'; } else if (ks == XK_KP_4) { *key = '4'; } else if (ks == XK_KP_5) { *key = '5'; } else if (ks == XK_KP_6) { *key = '6'; } else if (ks == XK_KP_7) { *key = '7'; } else if (ks == XK_KP_8) { *key = '8'; } else if (ks == XK_KP_9) { *key = '9'; #ifdef XK_Select } else if (ks == XK_Select) { *key = KBD_SELECT; #endif #ifdef XK_Undo } else if (ks == XK_Undo) { *key = KBD_UNDO; #endif #ifdef XK_Redo } else if (ks == XK_Redo) { *key = KBD_REDO; #endif #ifdef XK_Menu } else if (ks == XK_Menu) { *key = KBD_MENU; #endif #ifdef XK_Find } else if (ks == XK_Find) { *key = KBD_FIND; #endif #ifdef XK_Cancel } else if (ks == XK_Cancel) { *key = KBD_STOP; #endif #ifdef XK_Help } else if (ks == XK_Help) { *key = KBD_HELP; #endif } else if (ks & 0x8000) { unsigned char *str = (unsigned char *)XKeysymToString(ks); if (str) { if (!casestrcmp(str, cast_uchar "XF86Copy")) { *key = KBD_COPY; return 1; } if (!casestrcmp(str, cast_uchar "XF86Paste")) { *key = KBD_PASTE; return 1; } if (!casestrcmp(str, cast_uchar "XF86Cut")) { *key = KBD_CUT; return 1; } if (!casestrcmp(str, cast_uchar "SunProps")) { *key = KBD_PROPS; return 1; } if (!casestrcmp(str, cast_uchar "SunFront")) { *key = KBD_FRONT; return 1; } if (!casestrcmp(str, cast_uchar "SunOpen")) { *key = KBD_OPEN; return 1; } if (!casestrcmp(str, cast_uchar "XF86Search")) { *key = KBD_FIND; return 1; } if (!casestrcmp(str, cast_uchar "XF86Favorites")) { *key = KBD_BOOKMARKS; return 1; } if (!casestrcmp(str, cast_uchar "XF86Reload")) { *key = KBD_RELOAD; return 1; } if (!casestrcmp(str, cast_uchar "XF86Stop")) { *key = KBD_STOP; return 1; } if (!casestrcmp(str, cast_uchar "XF86Forward")) { *key = KBD_FORWARD; return 1; } if (!casestrcmp(str, cast_uchar "XF86Back")) { *key = KBD_BACK; return 1; } if (!casestrcmp(str, cast_uchar "XF86Open")) { *key = KBD_OPEN; return 1; } if (!casestrcmp(str, cast_uchar "XF86OpenURL")) { *key = KBD_OPEN; return 1; } if (!casestrcmp(str, cast_uchar "apLineDel")) { *key = KBD_DEL; return 1; } } return 0; } else { *key = *flag & KBD_CTRL ? (int)ks & 255 : trans_key(cast_uchar str, table); } return 1; } static void x_init_hash_table(void) { int a; for (a = 0; a < X_HASH_TABLE_SIZE; a++) { x_hash_table[a].count = 0; x_hash_table[a].pointer = DUMMY; } } static void x_clear_clipboard(void); static void x_free_hash_table(void) { int a; for (a = 0; a < X_HASH_TABLE_SIZE; a++) { if (x_hash_table[a].count) internal_error("x_free_hash_table: the table is not empty"); mem_free(x_hash_table[a].pointer); } x_clear_clipboard(); x_free_colors(); if (static_color_map) free(static_color_map), static_color_map = NULL; if (static_color_table) mem_free(static_color_table), static_color_table = NULL; if (static_color_struct) mem_free(static_color_struct), static_color_struct = NULL; if (color_map_16bit) mem_free(color_map_16bit), color_map_16bit = NULL; if (color_map_24bit) mem_free(color_map_24bit), color_map_24bit = NULL; if (x_display) { if (x_icon) XFreePixmap(x_display, x_icon), x_icon = 0; if (fake_window_initialized) XDestroyWindow(x_display,fake_window), fake_window_initialized = 0; if (x_normal_gc) XFreeGC(x_display,x_normal_gc), x_normal_gc = 0; if (x_copy_gc) XFreeGC(x_display,x_copy_gc), x_copy_gc = 0; if (x_drawbitmap_gc) XFreeGC(x_display,x_drawbitmap_gc), x_drawbitmap_gc = 0; if (x_scroll_gc) XFreeGC(x_display,x_scroll_gc), x_scroll_gc = 0; #ifdef X_INPUT_METHOD if (xim) XCloseIM(xim), xim = NULL; #endif XCloseDisplay(x_display), x_display = NULL; } if (x_driver_param) mem_free(x_driver_param), x_driver_param = NULL; if (x_display_string) mem_free(x_display_string), x_display_string = NULL; undo_pixmap_handler(); process_events_in_progress = 0; flush_in_progress = 0; unregister_bottom_half(x_process_events, NULL); unregister_bottom_half(x_do_flush, NULL); } /* returns graphics device structure which belonging to the window */ static struct graphics_device *x_find_gd(Window win) { int a, b; a=(int)win & (X_HASH_TABLE_SIZE - 1); for (b = 0; b < x_hash_table[a].count; b++) { if (get_window_info(x_hash_table[a].pointer[b])->window == win) return x_hash_table[a].pointer[b]; } return NULL; } /* adds graphics device to hash table */ static void x_add_to_table(struct graphics_device *dev) { int a = (int)get_window_info(dev)->window & (X_HASH_TABLE_SIZE - 1); int c = x_hash_table[a].count; if ((unsigned)c > MAXINT / sizeof(struct graphics_device *) - 1) overalloc(); x_hash_table[a].pointer = mem_realloc(x_hash_table[a].pointer, (c + 1) * sizeof(struct graphics_device *)); x_hash_table[a].pointer[c] = dev; x_hash_table[a].count++; } /* removes graphics device from table */ static void x_remove_from_table(Window win) { int a = (int)win & (X_HASH_TABLE_SIZE - 1); int b; for (b = 0; b < x_hash_table[a].count; b++) { if (get_window_info(x_hash_table[a].pointer[b])->window == win) { memmove(x_hash_table[a].pointer + b, x_hash_table[a].pointer + b + 1, (x_hash_table[a].count - b - 1) * sizeof(struct graphics_device *)); x_hash_table[a].count--; x_hash_table[a].pointer = mem_realloc(x_hash_table[a].pointer, x_hash_table[a].count * sizeof(struct graphics_device *)); return; } } internal_error("x_remove_from_table: window not found"); } static void x_clear_clipboard(void) { if (x_my_clipboard) { mem_free(x_my_clipboard); x_my_clipboard = NULL; } } static void x_update_driver_param(int w, int h) { int l=0; if (n_wins != 1) return; x_default_window_width = w; x_default_window_height = h; if (x_driver_param) mem_free(x_driver_param); x_driver_param = init_str(); add_num_to_str(&x_driver_param, &l, x_default_window_width); add_chr_to_str(&x_driver_param, &l, 'x'); add_num_to_str(&x_driver_param, &l, x_default_window_height); } static int x_decode_button(int b) { switch (b) { case 1: return B_LEFT; case 2: return B_MIDDLE; case 3: return B_RIGHT; case 4: return B_WHEELUP; case 5: return B_WHEELDOWN; case 6: return B_WHEELLEFT; case 7: return B_WHEELRIGHT; case 8: return B_FOURTH; case 9: return B_FIFTH; case 10: return B_SIXTH; } return -1; } static void x_process_events(void *data) { XEvent event; XEvent last_event; struct graphics_device *dev; int last_was_mouse; int replay_event = 0; #ifdef OPENVMS clear_events(x_fd, 0); #endif process_events_in_progress = 0; #ifdef SC_DEBUG MESSAGE("x_process_event\n"); #endif memset(&last_event, 0, sizeof last_event); /* against warning */ last_was_mouse=0; while (XPending(x_display) || replay_event) { if (replay_event) replay_event = 0; else XNextEvent(x_display, &event); if (last_was_mouse&&(event.type==ButtonPress||event.type==ButtonRelease)) /* this is end of mouse move block --- call mouse handler */ { int a,b; last_was_mouse=0; #ifdef X_DEBUG MESSAGE("(MotionNotify event)\n"); { char txt[256]; sprintf(txt,"x=%d y=%d\n",last_event.xmotion.x,last_event.xmotion.y); MESSAGE(txt); } #endif dev = x_find_gd(last_event.xmotion.window); if (!dev) break; a=B_LEFT; b=B_MOVE; if ((last_event.xmotion.state)&Button1Mask) { a=B_LEFT; b=B_DRAG; #ifdef X_DEBUG MESSAGE("left button/drag\n"); #endif } if ((last_event.xmotion.state)&Button2Mask) { a=B_MIDDLE; b=B_DRAG; #ifdef X_DEBUG MESSAGE("middle button/drag\n"); #endif } if ((last_event.xmotion.state)&Button3Mask) { a=B_RIGHT; b=B_DRAG; #ifdef X_DEBUG MESSAGE("right button/drag\n"); #endif } x_clip_number(&last_event.xmotion.x, dev->size.x1, dev->size.x2 - 1); x_clip_number(&last_event.xmotion.y, dev->size.y1, dev->size.y2 - 1); dev->mouse_handler(dev, last_event.xmotion.x, last_event.xmotion.y, a | b); } switch(event.type) { case GraphicsExpose: /* redraw uncovered area during scroll */ { struct rect r; #ifdef X_DEBUG MESSAGE("(GraphicsExpose event)\n"); #endif dev = x_find_gd(event.xgraphicsexpose.drawable); if (!dev) break; r.x1=event.xgraphicsexpose.x; r.y1=event.xgraphicsexpose.y; r.x2=event.xgraphicsexpose.x+event.xgraphicsexpose.width; r.y2=event.xgraphicsexpose.y+event.xgraphicsexpose.height; dev->redraw_handler(dev,&r); } break; case Expose: /* redraw part of the window */ { struct rect r; #ifdef X_DEBUG MESSAGE("(Expose event)\n"); #endif dev = x_find_gd(event.xexpose.window); if (!dev) break; r.x1=event.xexpose.x; r.y1=event.xexpose.y; r.x2=event.xexpose.x+event.xexpose.width; r.y2=event.xexpose.y+event.xexpose.height; dev->redraw_handler(dev,&r); } break; case ConfigureNotify: /* resize window */ #ifdef X_DEBUG MESSAGE("(ConfigureNotify event)\n"); { char txt[256]; sprintf(txt,"width=%d height=%d\n",event.xconfigure.width,event.xconfigure.height); MESSAGE(txt); } #endif dev = x_find_gd(event.xconfigure.window); if (!dev) break; /* when window only moved and size is the same, do nothing */ if (dev->size.x2==event.xconfigure.width&&dev->size.y2==event.xconfigure.height)break; configure_notify_again: dev->size.x2=event.xconfigure.width; dev->size.y2=event.xconfigure.height; x_update_driver_param(event.xconfigure.width, event.xconfigure.height); while (XCheckWindowEvent(x_display,get_window_info(dev)->window,ExposureMask,&event)==True) ; if (XCheckWindowEvent(x_display,get_window_info(dev)->window,StructureNotifyMask,&event)==True) { if (event.type==ConfigureNotify) goto configure_notify_again; replay_event=1; } dev->resize_handler(dev); break; case KeyPress: /* a key was pressed */ { int f,k; #ifdef X_DEBUG MESSAGE("(KeyPress event)\n"); { char txt[256]; sprintf(txt,"keycode=%d state=%d\n",event.xkey.keycode,event.xkey.state); MESSAGE(txt); } #endif #ifdef X_INPUT_METHOD if (XFilterEvent(&event, None)) break; #endif dev = x_find_gd(event.xkey.window); if (!dev) break; if (x_translate_key(dev, (XKeyEvent_p)&event, &k, &f)) dev->keyboard_handler(dev, k, f); } break; case ClientMessage: if ( event.xclient.format!=32|| event.xclient.message_type!=x_wm_protocols_atom|| (Atom)event.xclient.data.l[0]!=x_delete_window_atom )break; /*-fallthrough*/ /* This event is destroy window event from window manager */ case DestroyNotify: #ifdef X_DEBUG MESSAGE("(DestroyNotify event)\n"); #endif dev = x_find_gd(event.xkey.window); if (!dev) break; dev->keyboard_handler(dev,KBD_CLOSE,0); break; case ButtonRelease: /* mouse button was released */ { int a; #ifdef X_DEBUG MESSAGE("(ButtonRelease event)\n"); { char txt[256]; sprintf(txt,"x=%d y=%d buttons=%d mask=%d\n",event.xbutton.x,event.xbutton.y,event.xbutton.button,event.xbutton.state); MESSAGE(txt); } #endif dev = x_find_gd(event.xbutton.window); if (!dev) break; last_was_mouse = 0; if ((a = x_decode_button(event.xbutton.button)) >= 0 && !BM_IS_WHEEL(a)) { x_clip_number(&event.xmotion.x, dev->size.x1, dev->size.x2 - 1); x_clip_number(&event.xmotion.y, dev->size.y1, dev->size.y2 - 1); dev->mouse_handler(dev, event.xbutton.x, event.xbutton.y, a | B_UP); } } break; case ButtonPress: /* mouse button was pressed */ { int a; #ifdef X_DEBUG MESSAGE("(ButtonPress event)\n"); { char txt[256]; sprintf(txt,"x=%d y=%d buttons=%d mask=%d\n",event.xbutton.x,event.xbutton.y,event.xbutton.button,event.xbutton.state); MESSAGE(txt); } #endif dev = x_find_gd(event.xbutton.window); if (!dev) break; last_was_mouse = 0; if ((a = x_decode_button(event.xbutton.button)) >= 0) { x_clip_number(&(event.xmotion.x), dev->size.x1, dev->size.x2 - 1); x_clip_number(&(event.xmotion.y), dev->size.y1, dev->size.y2 - 1); dev->mouse_handler(dev, event.xbutton.x, event.xbutton.y, a | (!BM_IS_WHEEL(a) ? B_DOWN : B_MOVE)); } } break; case MotionNotify: /* pointer moved */ { #ifdef X_DEBUG MESSAGE("(MotionNotify event)\n"); { char txt[256]; sprintf(txt,"x=%d y=%d\n",event.xmotion.x,event.xmotion.y); MESSAGE(txt); } #endif /* just sign, that this was mouse event */ last_was_mouse=1; last_event=event; /* fix lag when using remote X and dragging over some text */ XSync(x_display, False); } break; /* read clipboard */ case SelectionNotify: #ifdef X_DEBUG MESSAGE("xselectionnotify\n"); #endif /* handled in x_get_clipboard_text */ break; /* This long code must be here in order to implement copying of stuff into the clipboard */ case SelectionRequest: { selection_request(&event); } break; case MapNotify: XFlush (x_display); break; default: #ifdef X_DEBUG { char txt[256]; sprintf(txt,"event=%d\n",event.type); MESSAGE(txt); } #endif break; } } if (last_was_mouse) /* that was end of mouse move block --- call mouse handler */ { int a,b; last_was_mouse=0; #ifdef X_DEBUG MESSAGE("(MotionNotify event)\n"); /* { char txt[256]; sprintf(txt,"x=%d y=%d\n",last_event.xmotion.x,last_event.xmotion.y); MESSAGE(txt); } */ #endif dev = x_find_gd(last_event.xmotion.window); if (!dev) goto ret; a=B_LEFT; b=B_MOVE; if ((last_event.xmotion.state)&Button1Mask) { a=B_LEFT; b=B_DRAG; #ifdef X_DEBUG MESSAGE("left button/drag\n"); #endif } if ((last_event.xmotion.state)&Button2Mask) { a=B_MIDDLE; b=B_DRAG; #ifdef X_DEBUG MESSAGE("middle button/drag\n"); #endif } if ((last_event.xmotion.state)&Button3Mask) { a=B_RIGHT; b=B_DRAG; #ifdef X_DEBUG MESSAGE("right button/drag\n"); #endif } x_clip_number(&last_event.xmotion.x, dev->size.x1, dev->size.x2 - 1); x_clip_number(&last_event.xmotion.y, dev->size.y1, dev->size.y2 - 1); dev->mouse_handler(dev,last_event.xmotion.x,last_event.xmotion.y,a|b); } ret:; #ifdef SC_DEBUG MESSAGE("x_process_event end\n"); #endif } static void x_after_fork(void) { int rs; if (x_fd != -1) { EINTRLOOP(rs, close(x_fd)); x_fd = -1; } } /* returns pointer to string with driver parameter or NULL */ static unsigned char *x_get_driver_param(void) { return x_driver_param; } static unsigned char *x_get_af_unix_name(void) { return x_display_string; } #ifdef X_INPUT_METHOD static XIC x_open_xic(Window w); #endif /* initiate connection with X server */ static unsigned char *x_init_driver(unsigned char *param, unsigned char *display) { unsigned char *err; int l; XGCValues gcv; XSetWindowAttributes win_attr; int misordered = -1; x_init_hash_table(); #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) setlocale(LC_CTYPE, ""); #endif #ifdef X_DEBUG { char txt[256]; sprintf(txt,"x_init_driver(%s, %s)\n", param, display); MESSAGE(txt); } #endif x_input_encoding=-1; #if defined(HAVE_NL_LANGINFO) && defined(HAVE_LANGINFO_H) && defined(CODESET) && !defined(WIN) && !defined(INTERIX) { unsigned char *cp; cp = cast_uchar nl_langinfo(CODESET); x_input_encoding=get_cp_index(cp); } #endif if (!display || !(*display)) display = NULL; /* X documentation says on XOpenDisplay(display_name) : display_name Specifies the hardware display name, which determines the dis- play and communications domain to be used. On a POSIX-confor- mant system, if the display_name is NULL, it defaults to the value of the DISPLAY environment variable. But OS/2 has problems when display_name is NULL ... */ if (!display) display = cast_uchar getenv("DISPLAY"); #if !defined(__linux__) && !defined(OPENVMS) /* on Linux, do not assume XWINDOW present if $DISPLAY is not set --- rather open links on svgalib or framebuffer console */ if (!display) display = cast_uchar ":0.0"; /* needed for MacOS X */ #endif x_display_string = stracpy(display ? display : cast_uchar ""); if (display) { char_p xx_display = x_dup(display, strlen(cast_const_char display) + 1); x_display = XOpenDisplay(xx_display); free(xx_display); } else { x_display = XOpenDisplay(NULL); } if (!x_display) { err = init_str(); l = 0; if (display) { add_to_str(&err, &l, cast_uchar "Can't open display \""); add_to_str(&err, &l, display); add_to_str(&err, &l, cast_uchar "\"\n"); } else { add_to_str(&err, &l, cast_uchar "Can't open default display\n"); } goto ret_err; } x_bitmap_bit_order = BitmapBitOrder(x_display); x_screen = DefaultScreen(x_display); x_display_height = DisplayHeight(x_display, x_screen); x_display_width = DisplayWidth(x_display, x_screen); x_root_window = RootWindow(x_display, x_screen); x_default_colormap = XDefaultColormap(x_display, x_screen); x_default_window_width = x_display_width; if (x_default_window_width >= 100) x_default_window_width -= 50; x_default_window_height = x_display_height; if (x_default_window_height >= 150) x_default_window_height -= 75; x_driver_param = NULL; if (param && *param) { unsigned char *e; char *end_c; unsigned long w, h; x_driver_param = stracpy(param); if (*x_driver_param < '0' || *x_driver_param > '9') { invalid_param: err = stracpy(cast_uchar "Invalid parameter.\n"); goto ret_err; } w = strtoul(cast_const_char x_driver_param, &end_c, 10); e = cast_uchar end_c; if (upcase(*e) != 'X') goto invalid_param; e++; if (*e < '0' || *e > '9') goto invalid_param; h = strtoul(cast_const_char e, &end_c, 10); e = cast_uchar end_c; if (*e) goto invalid_param; if (w && h && w <= 30000 && h <= 30000) { x_default_window_width=(int)w; x_default_window_height=(int)h; } } /* find best visual */ { static_const unsigned char depths[] = {24, 16, 15, 8, 4}; static_const int classes[] = {TrueColor, PseudoColor, StaticColor}; /* FIXME: dodelat DirectColor */ unsigned a, b; for (a = 0; a < array_elements(depths); a++) for (b = 0; b < array_elements(classes); b++) { if ((classes[b] == PseudoColor || classes[b] == StaticColor) && depths[a] > 8) continue; if (classes[b] == TrueColor && depths[a] <= 8) continue; if (XMatchVisualInfo(x_display, x_screen, depths[a], classes[b], &vinfo)) { XPixmapFormatValues_p pfm; int n, i; x_default_visual = vinfo.visual; x_depth = vinfo.depth; /* determine bytes per pixel */ pfm = XListPixmapFormats(x_display, &n); for (i = 0; i < n; i++) if (pfm[i].depth == x_depth) { x_bitmap_bpp = pfm[i].bits_per_pixel < 8 ? 1 : pfm[i].bits_per_pixel >> 3; x_bitmap_scanline_pad = pfm[i].scanline_pad >> 3; XFree(pfm); goto bytes_per_pixel_found; } if (n) XFree(pfm); continue; bytes_per_pixel_found: /* test misordered flag */ /*debug("x_depth %d, x_bitmap_bpp %d %lx %lx %lx %s", x_depth, x_bitmap_bpp, vinfo.red_mask, vinfo.green_mask, vinfo.blue_mask, x_bitmap_bit_order == MSBFirst ? "MSBFirst" : "LSBFirst");*/ switch (x_depth) { case 4: case 8: if (x_bitmap_bpp != 1) break; if (vinfo.class == StaticColor || vinfo.class == PseudoColor) { misordered = 0; goto visual_found; } break; case 15: case 16: if (x_bitmap_bpp != 2) break; if (x_depth == 16 && vinfo.red_mask == 0xf800 && vinfo.green_mask == 0x7e0 && vinfo.blue_mask == 0x1f) { misordered = x_bitmap_bit_order == MSBFirst ? 256 : 0; goto visual_found; } if (x_depth == 15 && vinfo.red_mask == 0x7c00 && vinfo.green_mask == 0x3e0 && vinfo.blue_mask == 0x1f) { misordered = x_bitmap_bit_order == MSBFirst ? 256 : 0; goto visual_found; } if (create_16bit_mapping(vinfo.red_mask, vinfo.green_mask, vinfo.blue_mask)) { misordered = 0; goto visual_found; } break; case 24: if (x_bitmap_bpp != 3 && x_bitmap_bpp != 4) break; if (vinfo.red_mask == 0xff0000 && vinfo.green_mask == 0xff00 && vinfo.blue_mask == 0xff) { misordered = x_bitmap_bpp == 4 && x_bitmap_bit_order == MSBFirst ? 512 : 0; goto visual_found; } if (create_24bit_mapping(vinfo.red_mask, vinfo.green_mask, vinfo.blue_mask)) { misordered = 0; goto visual_found; } break; } } } err = stracpy(cast_uchar "No supported color depth found.\n"); goto ret_err; } visual_found: x_driver.flags &= ~GD_SWITCH_PALETTE; x_have_palette = 0; x_use_static_color_table = 0; if (vinfo.class == StaticColor) { if (x_depth > 8) return stracpy(cast_uchar "Static color supported for up to 8-bit depth.\n"); if ((err = x_query_palette())) goto ret_err; x_use_static_color_table = 1; } if (vinfo.class == PseudoColor) { if (x_depth > 8) return stracpy(cast_uchar "Static color supported for up to 8-bit depth.\n"); x_use_static_color_table = !x_driver.param->palette_mode; x_have_palette = 1; x_colormap = XCreateColormap(x_display, x_root_window, x_default_visual, AllocAll); x_set_palette(); x_driver.flags |= GD_SWITCH_PALETTE; } x_driver.depth = 0; x_driver.depth |= x_bitmap_bpp; x_driver.depth |= x_depth << 3; x_driver.depth |= misordered; /* check if depth is sane */ if (x_use_static_color_table) x_driver.depth = X_STATIC_CODE; #ifdef X_DEBUG { char txt[256]; sprintf(txt,"x_driver.depth=%d\n",x_driver.depth); MESSAGE(txt); } #endif x_get_color_function = get_color_fn(x_driver.depth); if (!x_get_color_function) { unsigned char nevidim_te_ani_te_neslysim_ale_smrdis_jako_lejno[MAX_STR_LEN]; snprintf(cast_char nevidim_te_ani_te_neslysim_ale_smrdis_jako_lejno, MAX_STR_LEN, "Unsupported graphics mode: x_depth=%d, bits_per_pixel=%d, bytes_per_pixel=%d\n",x_driver.depth, x_depth, x_bitmap_bpp); err = stracpy(nevidim_te_ani_te_neslysim_ale_smrdis_jako_lejno); goto ret_err; } x_black_pixel = BlackPixel(x_display, x_screen); gcv.function = GXcopy; gcv.graphics_exposures = True; /* we want to receive GraphicsExpose events when uninitialized area is discovered during scroll */ gcv.fill_style = FillSolid; gcv.background = x_black_pixel; x_delete_window_atom = XInternAtom(x_display,"WM_DELETE_WINDOW", False); x_wm_protocols_atom = XInternAtom(x_display,"WM_PROTOCOLS", False); x_sel_atom = XInternAtom(x_display, "SEL_PROP", False); x_targets_atom = XInternAtom(x_display, "TARGETS", False); x_utf8_string_atom = XInternAtom(x_display, "UTF8_STRING", False); if (x_have_palette) win_attr.colormap = x_colormap; else win_attr.colormap = x_default_colormap; fake_window = XCreateWindow( x_display, x_root_window, 0, 0, 10, 10, 0, x_depth, CopyFromParent, x_default_visual, CWColormap, &win_attr ); fake_window_initialized = 1; x_normal_gc = XCreateGC(x_display, fake_window, GCFillStyle|GCBackground, &gcv); if (!x_normal_gc) { err = stracpy(cast_uchar "Cannot create graphic context.\n"); goto ret_err; } x_normal_gc_color = 0; XSetForeground(x_display, x_normal_gc, x_normal_gc_color); XSetLineAttributes(x_display, x_normal_gc, 1, LineSolid, CapRound, JoinRound); x_copy_gc = XCreateGC(x_display, fake_window, GCFunction, &gcv); if (!x_copy_gc) { err = stracpy(cast_uchar "Cannot create graphic context.\n"); goto ret_err; } x_drawbitmap_gc = XCreateGC(x_display, fake_window, GCFunction, &gcv); if (!x_drawbitmap_gc) { err = stracpy(cast_uchar "Cannot create graphic context.\n"); goto ret_err; } x_scroll_gc = XCreateGC(x_display, fake_window, GCGraphicsExposures|GCBackground, &gcv); if (!x_scroll_gc) { err = stracpy(cast_uchar "Cannot create graphic context.\n"); goto ret_err; } x_scroll_gc_rect.x1 = x_scroll_gc_rect.x2 = x_scroll_gc_rect.y1 = x_scroll_gc_rect.y2 = -1; #ifdef X_INPUT_METHOD { #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) /* * Unfortunatelly, dead keys are translated according to * current locale, even if we use Xutf8LookupString. * So, try to set locale to utf8 for the input method. */ unsigned char *l; int len; l = cast_uchar setlocale(LC_CTYPE, ""); len = l ? (int)strlen(cast_const_char l) : 0; if (l && !(len >= 5 && !casestrcmp(l + len - 5, cast_uchar ".utf8")) && !(len >= 6 && !casestrcmp(l + len - 6, cast_uchar ".utf-8")) ) { unsigned char *m = memacpy(l, strcspn(cast_const_char l, ".")); add_to_strn(&m, cast_uchar ".UTF-8"); l = cast_uchar setlocale(LC_CTYPE, cast_const_char m); mem_free(m); } if (!l) { l = cast_uchar setlocale(LC_CTYPE, "en_US.UTF-8"); if (!l) l = cast_uchar setlocale(LC_CTYPE, "C.UTF-8"); } #endif xim = XOpenIM(x_display, NULL, NULL, NULL); #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) if (!xim) { l = cast_uchar setlocale(LC_CTYPE, "en_US.UTF-8"); if (!l) l = cast_uchar setlocale(LC_CTYPE, "C.UTF-8"); xim = XOpenIM(x_display, NULL, NULL, NULL); } #endif if (xim) { XIC xic = x_open_xic(fake_window); if (xic) { XDestroyIC(xic); } else { XCloseIM(xim), xim = NULL; } } #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) setlocale(LC_CTYPE, ""); #endif } #endif if (x_input_encoding < 0 #ifdef X_INPUT_METHOD && !xim #endif ) x_driver.flags|=GD_NEED_CODEPAGE; x_fd = XConnectionNumber(x_display); #ifdef OPENVMS x_fd = vms_x11_fd(x_fd); #endif set_handlers(x_fd, x_process_events, NULL, NULL); setup_pixmap_handler(); XSync(x_display, False); X_SCHEDULE_PROCESS_EVENTS(); return NULL; ret_err: x_free_hash_table(); return err; } /* close connection with the X server */ static void x_shutdown_driver(void) { #ifdef X_DEBUG MESSAGE("x_shutdown_driver\n"); #endif set_handlers(x_fd, NULL, NULL, NULL); x_free_hash_table(); } #ifdef X_INPUT_METHOD static XIC x_open_xic(Window w) { return XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, w, XNFocusWindow, w, NULL); } #endif /* create new window */ static struct graphics_device *x_init_device(void) { struct graphics_device *dev; XWMHints wm_hints; XClassHint class_hints; XTextProperty windowName; char_p links_name = "Links"; XSetWindowAttributes win_attr; struct window_info *wi; #ifdef X_DEBUG MESSAGE("x_init_device\n"); #endif dev = mem_alloc(sizeof(struct graphics_device)); wi = mem_calloc(sizeof(struct window_info)); dev->size.x1 = 0; dev->size.y1 = 0; dev->size.x2 = x_default_window_width; dev->size.y2 = x_default_window_height; if (x_have_palette) win_attr.colormap = x_colormap; else win_attr.colormap = x_default_colormap; win_attr.border_pixel = x_black_pixel; x_prepare_for_failure(X_CreateWindow); wi->window = XCreateWindow( x_display, x_root_window, dev->size.x1, dev->size.y1, dev->size.x2, dev->size.y2, 0, x_depth, InputOutput, x_default_visual, CWColormap | CWBorderPixel, &win_attr ); if (x_test_for_failure()) { x_prepare_for_failure(X_DestroyWindow); XDestroyWindow(x_display, wi->window); x_test_for_failure(); mem_free(dev); mem_free(wi); return NULL; } if (!x_icon) { XImage_p img; unsigned char *data; char_p xx_data; int w, h; ssize_t skip; get_links_icon(&data, &w, &h, &skip, x_bitmap_scanline_pad); x_convert_to_216(data, w, h, skip); xx_data = x_dup(data, h * skip); mem_free(data); retry: img = XCreateImage(x_display, x_default_visual, x_depth, ZPixmap, 0, xx_data, w, h, x_bitmap_scanline_pad << 3, (int)skip); if (!img) { if (out_of_memory(0, NULL, 0)) goto retry; free(xx_data); x_icon = 0; goto nic_nebude_bobankove; } XSync(x_display, False); X_SCHEDULE_PROCESS_EVENTS(); x_prepare_for_failure(X_CreatePixmap); x_icon = XCreatePixmap(x_display, wi->window, w, h, x_depth); if (x_test_for_failure()) { x_prepare_for_failure(X_FreePixmap); XFreePixmap(x_display, x_icon); x_test_for_failure(); x_icon = 0; } if (!x_icon) { XDestroyImage(img); x_icon = 0; goto nic_nebude_bobankove; } XPutImage(x_display, x_icon, x_copy_gc, img, 0, 0, 0, 0, w, h); XDestroyImage(img); nic_nebude_bobankove:; } wm_hints.flags=InputHint; wm_hints.input=True; if (x_icon) { wm_hints.flags=InputHint|IconPixmapHint; wm_hints.icon_pixmap=x_icon; } XSetWMHints(x_display, wi->window, &wm_hints); class_hints.res_name = links_name; class_hints.res_class = links_name; XSetClassHint(x_display, wi->window, &class_hints); XStringListToTextProperty(&links_name, 1, &windowName); XSetWMName(x_display, wi->window, &windowName); XStoreName(x_display, wi->window, links_name); XSetWMIconName(x_display, wi->window, &windowName); XFree(windowName.value); XMapWindow(x_display,wi->window); dev->clip.x1=dev->size.x1; dev->clip.y1=dev->size.y1; dev->clip.x2=dev->size.x2; dev->clip.y2=dev->size.y2; dev->driver_data=wi; dev->user_data=0; XSetWindowBackgroundPixmap(x_display, wi->window, None); x_add_to_table(dev); XSetWMProtocols(x_display,wi->window,&x_delete_window_atom,1); XSelectInput(x_display,wi->window, ExposureMask| KeyPressMask| ButtonPressMask| ButtonReleaseMask| PointerMotionMask| ButtonMotionMask| StructureNotifyMask| 0 ); #ifdef X_INPUT_METHOD if (xim) { wi->xic = x_open_xic(wi->window); } #endif XSync(x_display, False); X_SCHEDULE_PROCESS_EVENTS(); n_wins++; return dev; } /* close window */ static void x_shutdown_device(struct graphics_device *dev) { struct window_info *wi = get_window_info(dev); #ifdef X_DEBUG MESSAGE("x_shutdown_device\n"); #endif n_wins--; XDestroyWindow(x_display, wi->window); #ifdef X_INPUT_METHOD if (wi->xic) { XDestroyIC(wi->xic); } #endif XSync(x_display, False); X_SCHEDULE_PROCESS_EVENTS(); x_remove_from_table(wi->window); mem_free(wi); mem_free(dev); } static void x_update_palette(void) { if (x_use_static_color_table == !x_driver.param->palette_mode) return; x_use_static_color_table = !x_driver.param->palette_mode; if (x_use_static_color_table) x_driver.depth = X_STATIC_CODE; else x_driver.depth = x_bitmap_bpp | (x_depth << 3); x_get_color_function = get_color_fn(x_driver.depth); if (!x_get_color_function) internal_error("x: unsupported depth %d", x_driver.depth); x_set_palette(); } static unsigned short *x_get_real_colors(void) { int perfect = 1; int i; unsigned short *v; if (!x_use_static_color_table) return NULL; v = mem_calloc(256 * 3 * sizeof(unsigned short)); for (i = 0; i < X_STATIC_COLORS; i++) { unsigned idx = static_color_table[i]; v[i * 3 + 0] = static_color_map[idx].red; v[i * 3 + 1] = static_color_map[idx].green; v[i * 3 + 2] = static_color_map[idx].blue; if (perfect) { unsigned rgb[256]; q_palette(X_STATIC_COLORS, i, 65535, rgb); if (static_color_map[idx].red != rgb[0] || static_color_map[idx].green != rgb[1] || static_color_map[idx].blue != rgb[2]) { /*fprintf(stderr, "imperfect %d: %04x,%04x,%04x - %04x,%04x,%04x\n", i, rgb[0], rgb[1], rgb[2], static_color_map[idx].red, static_color_map[idx].green, static_color_map[idx].blue);*/ perfect = 0; } } } if (perfect) { mem_free(v); return NULL; } return v; } static void x_translate_colors(unsigned char *data, int x, int y, ssize_t skip) { int i, j; if (color_map_16bit) { for (j = 0; j < y; j++) { for (i = 0; i < x; i++) { unsigned short s = color_map_16bit[data[2*i]] | color_map_16bit[data[2*i + 1] + 256]; data[2*i] = (unsigned char)s; data[2*i + 1] = (unsigned char)(s >> 8); } data += skip; } return; } if (color_map_24bit && x_bitmap_bpp == 3) { for (j = 0; j < y; j++) { for (i = 0; i < x; i++) { unsigned s = color_map_24bit[data[3*i]] | color_map_24bit[data[3*i + 1] + 256] | color_map_24bit[data[3*i + 2] + 256 * 2]; data[3*i] = (unsigned char)s; data[3*i + 1] = (unsigned char)(s >> 8); data[3*i + 2] = (unsigned char)(s >> 16); } data += skip; } return; } if (color_map_24bit && x_bitmap_bpp == 4) { for (j = 0; j < y; j++) { for (i = 0; i < x; i++) { unsigned s = color_map_24bit[data[4*i]] | color_map_24bit[data[4*i + 1] + 256] | color_map_24bit[data[4*i + 2] + 256 * 2]; data[4*i] = (unsigned char)s; data[4*i + 1] = (unsigned char)(s >> 8); data[4*i + 2] = (unsigned char)(s >> 16); data[4*i + 3] = (unsigned char)(s >> 24); } data += skip; } return; } if (x_use_static_color_table) { for (j = 0; j < y; j++) { for (i = 0; i < x; i++) data[i] = static_color_table[data[i]]; data += skip; } return; } } static void x_convert_to_216(unsigned char *data, int x, int y, ssize_t skip) { unsigned char color_table[256]; int i, j; if (!static_color_table) return; if (x_use_static_color_table) { x_translate_colors(data, x, y, skip); return; } memset(color_table, 255, sizeof color_table); for (j = 0; j < y; j++) { for (i = 0; i < x; i++) { unsigned char a = data[i]; unsigned char result; if (color_table[a] != 255) { result = color_table[a]; } else { unsigned rgb[3]; q_palette(256, a, 65535, rgb); result = (unsigned char)get_nearest_color(rgb); color_table[a] = result; } data[i] = result; } data += skip; } } static int x_get_empty_bitmap(struct bitmap *bmp) { struct x_pixmapa *p; int pad; #ifdef X_DEBUG MESSAGE("x_get_empty_bitmap\n"); #endif p = mem_alloc(sizeof(struct x_pixmapa)); p->type = X_TYPE_NOTHING; add_to_list(bitmaps, p); bmp->data = NULL; bmp->flags = p; if (bmp->x > (MAXINT - x_bitmap_scanline_pad) / x_bitmap_bpp) return -1; pad = x_bitmap_scanline_pad - ((bmp->x * x_bitmap_bpp) % x_bitmap_scanline_pad); if (pad == x_bitmap_scanline_pad) pad = 0; bmp->skip = (ssize_t)bmp->x * x_bitmap_bpp + pad; if (bmp->skip > MAXINT / bmp->y) return -1; bmp->data = x_malloc(bmp->skip * bmp->y, 1); if (!bmp->data) return -1; /* on error bmp->data should point to NULL */ return 0; } static void x_register_bitmap(struct bitmap *bmp) { struct x_pixmapa *p; XImage_p image; Pixmap pixmap = 0; /* shut up warning */ int can_create_pixmap; #ifdef X_DEBUG MESSAGE("x_register_bitmap\n"); #endif if (!bmp->data || !bmp->x || !bmp->y) goto cant_create; x_translate_colors(bmp->data, bmp->x, bmp->y, bmp->skip); /* alloc XImage in client's memory */ retry: image = XCreateImage(x_display, x_default_visual, x_depth, ZPixmap, 0, (char_p)bmp->data, bmp->x, bmp->y, x_bitmap_scanline_pad << 3, (int)bmp->skip); if (!image) { if (out_of_memory(0, NULL, 0)) goto retry; goto cant_create; } /* try to alloc XPixmap in server's memory */ can_create_pixmap = 1; if (bmp->x >= 32768 || bmp->y >= 32768) { can_create_pixmap = 0; goto no_pixmap; } if (!pixmap_mode) x_prepare_for_failure(X_CreatePixmap); pixmap = XCreatePixmap(x_display, fake_window, bmp->x, bmp->y /*+ !(random() % 200) * 32768*/, x_depth); if (!pixmap_mode && x_test_for_failure()) { if (pixmap) { x_prepare_for_failure(X_FreePixmap); XFreePixmap(x_display, pixmap); x_test_for_failure(); pixmap = 0; } } if (!pixmap) { can_create_pixmap = 0; } no_pixmap: p = XPIXMAPP(bmp->flags); if (can_create_pixmap) { #ifdef X_DEBUG MESSAGE("x_register_bitmap: creating pixmap\n"); #endif XPutImage(x_display, pixmap, x_copy_gc, image, 0, 0, 0, 0, bmp->x, bmp->y); XDestroyImage(image); p->type = X_TYPE_PIXMAP; p->data.pixmap = pixmap; } else { #ifdef X_DEBUG MESSAGE("x_register_bitmap: creating image\n"); #endif p->type = X_TYPE_IMAGE; p->data.image = image; } bmp->data = NULL; return; cant_create: if (bmp->data) free(bmp->data), bmp->data = NULL; return; } static void x_unregister_bitmap(struct bitmap *bmp) { struct x_pixmapa *p = XPIXMAPP(bmp->flags); #ifdef X_DEBUG MESSAGE("x_unregister_bitmap\n"); #endif switch (p->type) { case X_TYPE_NOTHING: break; case X_TYPE_PIXMAP: XFreePixmap(x_display, p->data.pixmap); /* free XPixmap from server's memory */ break; case X_TYPE_IMAGE: XDestroyImage(p->data.image); /* free XImage from client's memory */ break; default: internal_error("invalid pixmap type %d", (int)p->type); } del_from_list(p); mem_free(p); /* struct x_pixmap */ } static long x_get_color(int rgb) { long block; unsigned char *b; #ifdef X_DEBUG MESSAGE("x_get_color\n"); #endif block = x_get_color_function(rgb); b = (unsigned char *)█ x_translate_colors(b, 1, 1, 0); /*fprintf(stderr, "bitmap bpp %d\n", x_bitmap_bpp);*/ switch (x_bitmap_bpp) { case 1: return b[0]; case 2: if (x_bitmap_bit_order == LSBFirst) return (unsigned)b[0] | ((unsigned)b[1] << 8); else return (unsigned)b[1] | ((unsigned)b[0] << 8); case 3: if (x_bitmap_bit_order == LSBFirst) return (unsigned)b[0] | ((unsigned)b[1] << 8) | ((unsigned)b[2] << 16); else return (unsigned)b[2] | ((unsigned)b[1] << 8) | ((unsigned)b[0] << 16); default: if (x_bitmap_bit_order == LSBFirst) return (unsigned)b[0] | ((unsigned)b[1] << 8) | ((unsigned)b[2] << 16) | ((unsigned)b[3] << 24); else return (unsigned)b[3] | ((unsigned)b[2] << 8) | ((unsigned)b[1] << 16) | ((unsigned)b[0] << 24); } } static inline void x_set_color(long color) { if (color != x_normal_gc_color) { x_normal_gc_color = color; XSetForeground(x_display, x_normal_gc, color); } } static void x_fill_area(struct graphics_device *dev, int x1, int y1, int x2, int y2, long color) { #ifdef X_DEBUG { char txt[256]; sprintf(txt,"x_fill_area (x1=%d y1=%d x2=%d y2=%d)\n",x1,y1,x2,y2); MESSAGE(txt); } #endif CLIP_FILL_AREA x_set_color(color); XFillRectangle( x_display, get_window_info(dev)->window, x_normal_gc, x1, y1, x2 - x1, y2 - y1 ); X_FLUSH(); } static void x_draw_hline(struct graphics_device *dev, int x1, int y, int x2, long color) { #ifdef X_DEBUG MESSAGE("x_draw_hline\n"); #endif CLIP_DRAW_HLINE x_set_color(color); XDrawLine( x_display, get_window_info(dev)->window, x_normal_gc, x1, y, x2 - 1, y ); X_FLUSH(); } static void x_draw_vline(struct graphics_device *dev, int x, int y1, int y2, long color) { #ifdef X_DEBUG MESSAGE("x_draw_vline\n"); #endif CLIP_DRAW_VLINE x_set_color(color); XDrawLine( x_display, get_window_info(dev)->window, x_normal_gc, x, y1, x, y2 - 1 ); X_FLUSH(); } static void x_draw_bitmap(struct graphics_device *dev, struct bitmap *bmp, int x, int y) { struct x_pixmapa *p; int bmp_off_x, bmp_off_y, bmp_size_x, bmp_size_y; #ifdef X_DEBUG MESSAGE("x_draw_bitmap\n"); #endif CLIP_DRAW_BITMAP bmp_off_x = 0; bmp_off_y = 0; bmp_size_x = bmp->x; bmp_size_y = bmp->y; if (x < dev->clip.x1) { bmp_off_x = dev->clip.x1 - x; bmp_size_x -= dev->clip.x1 - x; x = dev->clip.x1; } if (x + bmp_size_x > dev->clip.x2) { bmp_size_x = dev->clip.x2 - x; } if (y < dev->clip.y1) { bmp_off_y = dev->clip.y1 - y; bmp_size_y -= dev->clip.y1 - y; y = dev->clip.y1; } if (y + bmp_size_y > dev->clip.y2) { bmp_size_y = dev->clip.y2 - y; } p = XPIXMAPP(bmp->flags); switch (p->type) { case X_TYPE_NOTHING: break; case X_TYPE_PIXMAP: XCopyArea(x_display, p->data.pixmap, get_window_info(dev)->window, x_drawbitmap_gc, bmp_off_x, bmp_off_y, bmp_size_x, bmp_size_y, x, y); break; case X_TYPE_IMAGE: XPutImage(x_display, get_window_info(dev)->window, x_drawbitmap_gc, p->data.image, bmp_off_x, bmp_off_y, x, y, bmp_size_x, bmp_size_y); break; default: internal_error("invalid pixmap type %d", (int)p->type); } X_FLUSH(); } static void *x_prepare_strip(struct bitmap *bmp, int top, int lines) { struct x_pixmapa *p = XPIXMAPP(bmp->flags); XImage *image; void_p x_data; #ifdef X_DEBUG MESSAGE("x_prepare_strip\n"); #endif bmp->data = NULL; switch (p->type) { case X_TYPE_NOTHING: return NULL; case X_TYPE_PIXMAP: x_data = x_malloc(bmp->skip * lines, 1); if (!x_data) { return NULL; } retry2: image = XCreateImage(x_display, x_default_visual, x_depth, ZPixmap, 0, x_data, bmp->x, lines, x_bitmap_scanline_pad << 3, (int)bmp->skip); if (!image) { if (out_of_memory(0, NULL, 0)) goto retry2; free(x_data); return NULL; } bmp->data = image; return image->data; case X_TYPE_IMAGE: return p->data.image->data+(bmp->skip*top); } internal_error("invalid pixmap type %d", (int)p->type); return NULL; } static void x_commit_strip(struct bitmap *bmp, int top, int lines) { struct x_pixmapa *p = XPIXMAPP(bmp->flags); #ifdef X_DEBUG MESSAGE("x_commit_strip\n"); #endif switch (p->type) { case X_TYPE_NOTHING: return; case X_TYPE_PIXMAP: /* send image to pixmap in xserver */ if (!bmp->data) return; x_translate_colors((unsigned char *)((XImage *)bmp->data)->data, bmp->x, lines, bmp->skip); XPutImage(x_display, p->data.pixmap, x_copy_gc, (XImage_p)bmp->data, 0, 0, 0, top, bmp->x, lines); XDestroyImage((XImage_p)bmp->data); return; case X_TYPE_IMAGE: x_translate_colors((unsigned char *)p->data.image->data + (bmp->skip * top), bmp->x, lines, bmp->skip); /* everything has been done by user */ return; } internal_error("invalid pixmap type %d", (int)p->type); } static int x_scroll(struct graphics_device *dev, struct rect_set **set, int scx, int scy) { XEvent ev; struct rect r; if (memcmp(&dev->clip, &x_scroll_gc_rect, sizeof(struct rect))) { XRectangle xr; memcpy(&x_scroll_gc_rect, &dev->clip, sizeof(struct rect)); xr.x = dev->clip.x1; xr.y = dev->clip.y1; xr.width = dev->clip.x2 - dev->clip.x1; xr.height = dev->clip.y2 - dev->clip.y1; XSetClipRectangles(x_display, x_scroll_gc, 0, 0, &xr, 1, Unsorted); } XCopyArea( x_display, get_window_info(dev)->window, get_window_info(dev)->window, x_scroll_gc, dev->clip.x1, dev->clip.y1, dev->clip.x2 - dev->clip.x1, dev->clip.y2 - dev->clip.y1, dev->clip.x1 + scx, dev->clip.y1 + scy ); XSync(x_display, False); /* ten sync tady musi byt, protoze potrebuju zarucit, aby vsechny * graphics-expose vyvolane timto scrollem byly vraceny v rect-set */ /* take all graphics expose events for this window and put them into the rect set */ while (XCheckWindowEvent(x_display, get_window_info(dev)->window, ExposureMask, &ev) == True) { switch(ev.type) { case GraphicsExpose: r.x1 = ev.xgraphicsexpose.x; r.y1 = ev.xgraphicsexpose.y; r.x2 = ev.xgraphicsexpose.x + ev.xgraphicsexpose.width; r.y2 = ev.xgraphicsexpose.y + ev.xgraphicsexpose.height; break; case Expose: r.x1 = ev.xexpose.x; r.y1 = ev.xexpose.y; r.x2 = ev.xexpose.x + ev.xexpose.width; r.y2 = ev.xexpose.y + ev.xexpose.height; break; default: continue; } if (r.x1 < dev->clip.x1 || r.x2 > dev->clip.x2 || r.y1 < dev->clip.y1 || r.y2 > dev->clip.y2) { /*fprintf(stderr, "rect out of scoll area: (%d,%d,%d,%d) - (%d,%d,%d,%d)\n", r.x1, r.x2, r.y1, r.y2, dev->clip.x1, dev->clip.x2, dev->clip.y1, dev->clip.y2);*/ switch(ev.type) { case GraphicsExpose: ev.xgraphicsexpose.x = 0; ev.xgraphicsexpose.y = 0; ev.xgraphicsexpose.width = dev->size.x2; ev.xgraphicsexpose.height = dev->size.y2; break; case Expose: ev.xexpose.x = 0; ev.xexpose.y = 0; ev.xexpose.width = dev->size.x2; ev.xexpose.height = dev->size.y2; break; } XPutBackEvent(x_display, &ev); mem_free(*set); *set = init_rect_set(); break; } add_to_rect_set(set, &r); } X_SCHEDULE_PROCESS_EVENTS(); #ifdef SC_DEBUG MESSAGE("hscroll\n"); #endif return 1; } static void x_flush(struct graphics_device *dev) { unregister_bottom_half(x_do_flush, NULL); x_do_flush(NULL); } static void x_set_window_title(struct graphics_device *dev, unsigned char *title) { unsigned char *t; char_p xx_str; XTextProperty windowName; int output_encoding; Status ret; #if defined(HAVE_XSUPPORTSLOCALE) && defined(HAVE_XMBTEXTLISTTOTEXTPROPERTY) if (XSupportsLocale()) { output_encoding = x_input_encoding >= 0 ? x_input_encoding : 0; } else retry_encode_ascii: #endif { output_encoding = 0; } if (!dev)internal_error("x_set_window_title called with NULL graphics_device pointer.\n"); t = convert(utf8_table, output_encoding, title, NULL); clr_white(t); /*XStoreName(x_display,get_window_info(dev)->window,"blabla");*/ xx_str = x_dup(t, strlen(cast_const_char t) + 1); mem_free(t); #if defined(HAVE_XSUPPORTSLOCALE) && defined(HAVE_XMBTEXTLISTTOTEXTPROPERTY) if (XSupportsLocale()) { ret = XmbTextListToTextProperty(x_display, &xx_str, 1, XStdICCTextStyle, &windowName); #ifdef X_HAVE_UTF8_STRING if (ret > 0) { XFree(windowName.value); ret = XmbTextListToTextProperty(x_display, &xx_str, 1, XUTF8StringStyle, &windowName); if (ret < 0) { ret = XmbTextListToTextProperty(x_display, &xx_str, 1, XStdICCTextStyle, &windowName); } } #endif if (ret < 0) { if (output_encoding) { free(xx_str); goto retry_encode_ascii; } else { goto retry_print_ascii; } } } else retry_print_ascii: #endif { ret = XStringListToTextProperty(&xx_str, 1, &windowName); if (!ret) { free(xx_str); return; } } free(xx_str); XSetWMName(x_display, get_window_info(dev)->window, &windowName); XSetWMIconName(x_display, get_window_info(dev)->window, &windowName); XFree(windowName.value); X_FLUSH(); } /* gets string in UTF8 */ static void x_set_clipboard_text(unsigned char *text) { x_clear_clipboard(); if (text) { x_my_clipboard = stracpy(text); XSetSelectionOwner(x_display, XA_PRIMARY, fake_window, CurrentTime); XFlush (x_display); X_SCHEDULE_PROCESS_EVENTS(); } } static void selection_request(XEvent *event) { XSelectionRequestEvent *req; XSelectionEvent sel; size_t l; unsigned_char_p xx_str; req = &(event->xselectionrequest); sel.type = SelectionNotify; sel.requestor = req->requestor; sel.selection = XA_PRIMARY; sel.target = req->target; sel.property = req->property; sel.time = req->time; sel.display = req->display; #ifdef X_DEBUG { char txt[256]; sprintf (txt,"xselectionrequest from %i\n",(int)event->xselection.requestor); MESSAGE(txt); sprintf (txt,"property:%i target:%i selection:%i\n", req->property,req->target, req->selection); MESSAGE(txt); } #endif x_prepare_for_failure(X_ChangeProperty); if (req->target == XA_STRING) { unsigned char *str, *p; if (!x_my_clipboard) str = stracpy(cast_uchar ""); else str = convert(utf8_table, get_cp_index(cast_uchar "iso-8859-1"), x_my_clipboard, NULL); for (p = cast_uchar strchr(cast_const_char str, 1); p; p = cast_uchar strchr(cast_const_char(str + 1), 1)) *p = 0xa0; l = strlen(cast_const_char str); if (l > X_MAX_CLIPBOARD_SIZE) l = X_MAX_CLIPBOARD_SIZE; xx_str = x_dup(str, l); XChangeProperty (x_display, sel.requestor, sel.property, XA_STRING, 8, PropModeReplace, xx_str, (int)l ); mem_free(str); free(xx_str); } else if (req->target == x_utf8_string_atom) { l = x_my_clipboard ? strlen(cast_const_char x_my_clipboard) : 0; if (l > X_MAX_CLIPBOARD_SIZE) l = X_MAX_CLIPBOARD_SIZE; xx_str = x_dup(x_my_clipboard, l); XChangeProperty (x_display, sel.requestor, sel.property, x_utf8_string_atom, 8, PropModeReplace, xx_str, (int)l ); free(xx_str); } else if (req->target == x_targets_atom) { unsigned tgt_atoms[3]; tgt_atoms[0] = (unsigned)x_targets_atom; tgt_atoms[1] = XA_STRING; tgt_atoms[2] = (unsigned)x_utf8_string_atom; XChangeProperty (x_display, sel.requestor, sel.property, XA_ATOM, 32, PropModeReplace, (unsigned_char_p)&tgt_atoms, 3 ); } else { #ifdef X_DEBUG { char txt[256]; sprintf (txt,"Non-String wanted: %i\n",(int)req->target); MESSAGE(txt); } #endif sel.property = None; } if (x_test_for_failure()) return; x_prepare_for_failure(X_SendEvent); XSendEvent(x_display, sel.requestor, 0, 0, (XEvent_p)&sel); x_test_for_failure(); } static unsigned char *x_get_clipboard_text(void) { XEvent event; Atom type_atom = x_utf8_string_atom; uttime t; retry: XConvertSelection(x_display, XA_PRIMARY, type_atom, x_sel_atom, fake_window, CurrentTime); t = get_time(); while (1) { uttime tt; int w; XSync(x_display, False); if (XCheckTypedEvent(x_display,SelectionRequest, &event)) { selection_request(&event); continue; } if (XCheckTypedEvent(x_display,SelectionNotify, &event)) break; tt = get_time() - t; if (tt > SELECTION_NOTIFY_TIMEOUT) w = 0; else w = (SELECTION_NOTIFY_TIMEOUT - (unsigned)tt + 999) / 1000; if (!x_wait_for_event(w)) goto no_new_sel; } if (event.xselection.property) { unsigned_char_p buffer; unsigned long pty_size, pty_items; int pty_format, ret; Atom pty_type; if (event.xselection.target != type_atom) goto no_new_sel; if (event.xselection.property != x_sel_atom) goto no_new_sel; /* Get size and type of property */ ret = XGetWindowProperty( x_display, fake_window, event.xselection.property, 0, 0, False, AnyPropertyType, &pty_type, &pty_format, &pty_items, &pty_size, &buffer); if (ret != Success) goto no_new_sel; XFree(buffer); ret = XGetWindowProperty( x_display, fake_window, event.xselection.property, 0, (long)pty_size, True, AnyPropertyType, &pty_type, &pty_format, &pty_items, &pty_size, &buffer ); if (ret != Success) goto no_new_sel; pty_size = (pty_format>>3) * pty_items; x_clear_clipboard(); if (type_atom == x_utf8_string_atom) { x_my_clipboard = stracpy(buffer); } else { x_my_clipboard = convert(get_cp_index(cast_uchar "iso-8859-1"), utf8_table, buffer, NULL); } XFree(buffer); } else { if (type_atom == x_utf8_string_atom) { type_atom = XA_STRING; goto retry; } } no_new_sel: X_SCHEDULE_PROCESS_EVENTS(); if (!x_my_clipboard) return NULL; return stracpy(x_my_clipboard); } struct graphics_driver x_driver = { cast_uchar "x", x_init_driver, x_init_device, x_shutdown_device, x_shutdown_driver, NULL, x_after_fork, x_get_driver_param, x_get_af_unix_name, NULL, NULL, x_get_empty_bitmap, x_register_bitmap, x_prepare_strip, x_commit_strip, x_unregister_bitmap, x_draw_bitmap, x_get_color, x_fill_area, x_draw_hline, x_draw_vline, x_scroll, NULL, x_flush, NULL, /* block */ NULL, /* unblock */ x_update_palette, x_get_real_colors, x_set_window_title, x_exec, x_set_clipboard_text, x_get_clipboard_text, 0, /* depth (filled in x_init_driver function) */ 0, 0, /* size (in X is empty) */ GD_UNICODE_KEYS, /* flags */ NULL, /* param */ }; #endif /* GRDRV_X */