links/x.c

3022 lines
77 KiB
C

/* 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 <X11/Xlib.h>
#include <X11/X.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/Xproto.h>
#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 <X11/Xlocale.h>
#else
#ifdef HAVE_SETLOCALE
#undef HAVE_SETLOCALE
#endif
#endif
#ifndef XK_MISCELLANY
#define XK_MISCELLANY
#endif
#ifndef XK_LATIN1
#define XK_LATIN1
#endif
#include <X11/keysymdef.h>
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 <langinfo.h>
#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 */
static void x_clip_number(int *n, int l, int h)
{
if (*n > 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 *)&block;
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 */