Skip to content
Snippets Groups Projects
x_events.c 59.1 KiB
Newer Older
deuce's avatar
deuce committed
/*
 * This file contains ONLY the functions that are called from the
 * event thread.
 */
 
Deucе's avatar
Deucе committed
#include <math.h>
deuce's avatar
deuce committed
#include <unistd.h>
#include <stdbool.h>
deuce's avatar
deuce committed

#include <fcntl.h>
#include <limits.h>
deuce's avatar
deuce committed
#include <locale.h>
deuce's avatar
deuce committed
#include <stdio.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
deuce's avatar
deuce committed

#include <threadwrap.h>
#include <genwrap.h>
#include <dirwrap.h>

#include "vidmodes.h"

#include "ciolib.h"
#define BITMAP_CIOLIB_DRIVER
deuce's avatar
deuce committed
#include "bitmap_con.h"
#include "link_list.h"
#include "scale.h"
deuce's avatar
deuce committed
#include "x_events.h"
#include "x_cio.h"
#include "utf8_codepages.h"
deuce's avatar
deuce committed

static bool send_fullscreen(bool set);
static void resize_window();

deuce's avatar
deuce committed
/*
 * Exported variables 
 */

int local_pipe[2];			/* Used for passing local events */
int key_pipe[2];			/* Used for passing keyboard events */

struct x11 x11;

char 	*copybuf;
pthread_mutex_t	copybuf_mutex;
char 	*pastebuf;
sem_t	pastebuf_set;
sem_t	pastebuf_used;
sem_t	init_complete;
sem_t	mode_set;
int x11_window_xpos;
int x11_window_ypos;
int x11_initialized=0;
static sem_t	event_thread_complete;
static int	terminate = 0;
static Atom	pastebuf_format;
Deucе's avatar
Deucе committed
bool xrender_found;
bool xinerama_found;
bool xrandr_found;
Deucе's avatar
Deucе committed
bool x_internal_scaling = true;
deuce's avatar
deuce committed
/*
 * Local variables
 */

enum UsedAtom {
	// UTF8_STRING: https://www.pps.jussieu.fr/~jch/software/UTF8_STRING/UTF8_STRING.text
	ATOM_UTF8_STRING,
	// ICCM: https://x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html
	ATOM_TARGETS,
	ATOM_WM_CLASS,
	ATOM_WM_DELETE_WINDOW,
	// EWMH: https://specifications.freedesktop.org/wm-spec/wm-spec-1.3.html
	ATOM__NET_FRAME_EXTENTS,
	ATOM__NET_SUPPORTED,
	ATOM__NET_SUPPORTING_WM_CHECK,
	ATOM__NET_WM_DESKTOP,
	ATOM__NET_WM_ICON,
	ATOM__NET_WM_ICON_NAME,
	ATOM__NET_WM_NAME,
	ATOM__NET_WM_PID,
	ATOM__NET_WM_PING,
	ATOM__NET_WM_STATE,
	ATOM__NET_WM_STATE_FULLSCREEN,
	ATOM__NET_WM_STATE_MAXIMIZED_VERT,
	ATOM__NET_WM_STATE_MAXIMIZED_HORZ,
	ATOM__NET_WM_WINDOW_TYPE,
	ATOM__NET_WM_WINDOW_TYPE_NORMAL,
	ATOM__NET_WORKAREA,
	ATOMCOUNT
};

enum AtomStandard {
	UTF8_ATOM,
	ICCM_ATOM,
	EWMH_ATOM
};

static uint32_t supported_standards;

#define XA_UTF8_STRING (0x10000000UL)

static struct AtomDef {
	const char * const name;
	const enum AtomStandard standard;
	Atom req_type;
	const int format;
	Atom atom;
	bool supported;
} SupportedAtoms[ATOMCOUNT] = {
	// UTF8_STRING: https://www.pps.jussieu.fr/~jch/software/UTF8_STRING/UTF8_STRING.text
	{"UTF8_STRING", UTF8_ATOM, None, 0, None, false},
	// ICCM: https://x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html
	{"CLIPBOARD", ICCM_ATOM, XA_ATOM, 32, None, false},
	{"TARGETS", ICCM_ATOM, XA_ATOM, 32, None, false},
	{"WM_CLASS", ICCM_ATOM, XA_STRING, 8, None, false},
	{"WM_DELETE_WINDOW", ICCM_ATOM, None, 0, None, false},
	// EWMH: https://specifications.freedesktop.org/wm-spec/wm-spec-1.3.html
	{"_NET_FRAME_EXTENTS", EWMH_ATOM, XA_CARDINAL, 32, None, false},
	{"_NET_SUPPORTED", EWMH_ATOM, XA_ATOM, 32, None, false},
	{"_NET_SUPPORTING_WM_CHECK", EWMH_ATOM, XA_WINDOW, 32, None, false},
	{"_NET_WM_DESKTOP", EWMH_ATOM, XA_CARDINAL, 32, None, false},
	{"_NET_WM_ICON", EWMH_ATOM, XA_CARDINAL, 32, None, false},
	{"_NET_WM_ICON_NAME", EWMH_ATOM, XA_UTF8_STRING, 8, None, false},
	{"_NET_WM_NAME", EWMH_ATOM, XA_UTF8_STRING, 8, None, false},
	{"_NET_WM_PID", EWMH_ATOM, XA_CARDINAL, 32, None, false},
	{"_NET_WM_PING", EWMH_ATOM, None, 0, None, false},
	{"_NET_WM_STATE", EWMH_ATOM, XA_ATOM, 32, None, false},
	{"_NET_WM_STATE_FULLSCREEN", EWMH_ATOM, None, 0, None, false},
	{"_NET_WM_STATE_MAXIMIZED_VERT", EWMH_ATOM, None, 0, None, false},
	{"_NET_WM_STATE_MAXIMIZED_HORZ", EWMH_ATOM, None, 0, None, false},
	{"_NET_WM_WINDOW_TYPE", EWMH_ATOM, XA_ATOM, 32, None, false},
	{"_NET_WM_WINDOW_TYPE_NORMAL", EWMH_ATOM, None, 0, None, false},
	{"_NET_WORKAREA", EWMH_ATOM, XA_CARDINAL, 4, None, false},
};

#define A(name) SupportedAtoms[ATOM_ ## name].atom

deuce's avatar
deuce committed
/* Sets the atom to be used for copy/paste operations */
static const long _NET_WM_STATE_REMOVE = 0;
static const long _NET_WM_STATE_ADD = 1;
deuce's avatar
deuce committed

static Display *dpy=NULL;
static Window win;
static Cursor curs = None;
Deucе's avatar
Deucе committed
static Visual *visual;
static Colormap wincmap;
static Pixmap icn;
static Pixmap icn_mask;
static bool VisualIsRGB8 = false;
deuce's avatar
deuce committed
static XImage *xim;
deuce's avatar
deuce committed
static XIM im;
static XIC ic;
deuce's avatar
deuce committed
static unsigned int depth=0;
static int xfd;
static unsigned long black;
static unsigned long white;
struct video_stats x_cvstat;
static unsigned long base_pixel;
static int r_shift;
static int g_shift;
static int b_shift;
static struct graphics_buffer *last = NULL;
Deucе's avatar
Deucе committed
#ifdef WITH_XRENDER
static XRenderPictFormat *xrender_pf = NULL;
static Pixmap xrender_pm = None;
static Picture xrender_src_pict = None;
static Picture xrender_dst_pict = None;
#endif
static bool fullscreen;
static Window parent;
static Window root;
static char *wm_wm_name;
static Atom copy_paste_selection = XA_PRIMARY;
static bool map_pending = true;
static int pending_width, pending_height, pending_xpos, pending_ypos;
deuce's avatar
deuce committed
/* Array of Graphics Contexts */
static GC gc;
deuce's avatar
deuce committed

static WORD Ascii2Scan[] = {
 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
 0x000e, 0x000f, 0xffff, 0xffff, 0xffff, 0x001c, 0xffff, 0xffff,
 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
 0xffff, 0xffff, 0xffff, 0x0001, 0xffff, 0xffff, 0xffff, 0xffff,
 0x0039, 0x0102, 0x0128, 0x0104, 0x0105, 0x0106, 0x0108, 0x0028,
 0x010a, 0x010b, 0x0109, 0x010d, 0x0033, 0x000c, 0x0034, 0x0035,
 0x000b, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008,
 0x0009, 0x000a, 0x0127, 0x0027, 0x0133, 0x000d, 0x0134, 0x0135,
 0x0103, 0x011e, 0x0130, 0x012e, 0x0120, 0x0112, 0x0121, 0x0122,
 0x0123, 0x0117, 0x0124, 0x0125, 0x0126, 0x0132, 0x0131, 0x0118,
 0x0119, 0x0110, 0x0113, 0x011f, 0x0114, 0x0116, 0x012f, 0x0111,
 0x012d, 0x0115, 0x012c, 0x001a, 0x002b, 0x001b, 0x0107, 0x010c,
 0x0029, 0x001e, 0x0030, 0x002e, 0x0020, 0x0012, 0x0021, 0x0022,
 0x0023, 0x0017, 0x0024, 0x0025, 0x0026, 0x0032, 0x0031, 0x0018,
 0x0019, 0x0010, 0x0013, 0x001f, 0x0014, 0x0016, 0x002f, 0x0011,
 0x002d, 0x0015, 0x002c, 0x011a, 0x012b, 0x011b, 0x0129, 0xffff,
};

static struct {
    WORD	base;
    WORD	shift;
    WORD	ctrl;
    WORD	alt;
} ScanCodes[] = {
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key  0 */
    {	0x011b, 0x011b, 0x011b, 0xffff }, /* key  1 - Escape key */
    {	0x0231, 0x0221, 0xffff, 0x7800 }, /* key  2 - '1' */
    {	0x0332, 0x0340, 0x0300, 0x7900 }, /* key  3 - '2' - special handling */
    {	0x0433, 0x0423, 0xffff, 0x7a00 }, /* key  4 - '3' */
    {	0x0534, 0x0524, 0xffff, 0x7b00 }, /* key  5 - '4' */
    {	0x0635, 0x0625, 0xffff, 0x7c00 }, /* key  6 - '5' */
    {	0x0736, 0x075e, 0x071e, 0x7d00 }, /* key  7 - '6' */
    {	0x0837, 0x0826, 0xffff, 0x7e00 }, /* key  8 - '7' */
    {	0x0938, 0x092a, 0xffff, 0x7f00 }, /* key  9 - '8' */
    {	0x0a39, 0x0a28, 0xffff, 0x8000 }, /* key 10 - '9' */
    {	0x0b30, 0x0b29, 0xffff, 0x8100 }, /* key 11 - '0' */
    {	0x0c2d, 0x0c5f, 0x0c1f, 0x8200 }, /* key 12 - '-' */
    {	0x0d3d, 0x0d2b, 0xffff, 0x8300 }, /* key 13 - '=' */
    {	0x0e08, 0x0e08, 0x0e7f, 0xffff }, /* key 14 - backspace */
    {	0x0f09, 0x0f00, 0xffff, 0xffff }, /* key 15 - tab */
    {	0x1071, 0x1051, 0x1011, 0x1000 }, /* key 16 - 'Q' */
    {	0x1177, 0x1157, 0x1117, 0x1100 }, /* key 17 - 'W' */
    {	0x1265, 0x1245, 0x1205, 0x1200 }, /* key 18 - 'E' */
    {	0x1372, 0x1352, 0x1312, 0x1300 }, /* key 19 - 'R' */
    {	0x1474, 0x1454, 0x1414, 0x1400 }, /* key 20 - 'T' */
    {	0x1579, 0x1559, 0x1519, 0x1500 }, /* key 21 - 'Y' */
    {	0x1675, 0x1655, 0x1615, 0x1600 }, /* key 22 - 'U' */
    {	0x1769, 0x1749, 0x1709, 0x1700 }, /* key 23 - 'I' */
    {	0x186f, 0x184f, 0x180f, 0x1800 }, /* key 24 - 'O' */
    {	0x1970, 0x1950, 0x1910, 0x1900 }, /* key 25 - 'P' */
    {	0x1a5b, 0x1a7b, 0x1a1b, 0xffff }, /* key 26 - '[' */
    {	0x1b5d, 0x1b7d, 0x1b1d, 0xffff }, /* key 27 - ']' */
    {	0x1c0d, 0x1c0d, 0x1c0a, 0xffff }, /* key 28 - CR */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 29 - control */
    {	0x1e61, 0x1e41, 0x1e01, 0x1e00 }, /* key 30 - 'A' */
    {	0x1f73, 0x1f53, 0x1f13, 0x1f00 }, /* key 31 - 'S' */
    {	0x2064, 0x2044, 0x2004, 0x2000 }, /* key 32 - 'D' */
    {	0x2166, 0x2146, 0x2106, 0x2100 }, /* key 33 - 'F' */
    {	0x2267, 0x2247, 0x2207, 0x2200 }, /* key 34 - 'G' */
    {	0x2368, 0x2348, 0x2308, 0x2300 }, /* key 35 - 'H' */
    {	0x246a, 0x244a, 0x240a, 0x2400 }, /* key 36 - 'J' */
    {	0x256b, 0x254b, 0x250b, 0x2500 }, /* key 37 - 'K' */
    {	0x266c, 0x264c, 0x260c, 0x2600 }, /* key 38 - 'L' */
    {	0x273b, 0x273a, 0xffff, 0xffff }, /* key 39 - ';' */
    {	0x2827, 0x2822, 0xffff, 0xffff }, /* key 40 - ''' */
    {	0x2960, 0x297e, 0xffff, 0xffff }, /* key 41 - '`' */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 42 - left shift */
    {	0x2b5c, 0x2b7c, 0x2b1c, 0xffff }, /* key 43 - '' */
    {	0x2c7a, 0x2c5a, 0x2c1a, 0x2c00 }, /* key 44 - 'Z' */
    {	0x2d78, 0x2d58, 0x2d18, 0x2d00 }, /* key 45 - 'X' */
    {	0x2e63, 0x2e43, 0x2e03, 0x2e00 }, /* key 46 - 'C' */
    {	0x2f76, 0x2f56, 0x2f16, 0x2f00 }, /* key 47 - 'V' */
    {	0x3062, 0x3042, 0x3002, 0x3000 }, /* key 48 - 'B' */
    {	0x316e, 0x314e, 0x310e, 0x3100 }, /* key 49 - 'N' */
    {	0x326d, 0x324d, 0x320d, 0x3200 }, /* key 50 - 'M' */
    {	0x332c, 0x333c, 0xffff, 0xffff }, /* key 51 - ',' */
    {	0x342e, 0x343e, 0xffff, 0xffff }, /* key 52 - '.' */
    {	0x352f, 0x353f, 0xffff, 0xffff }, /* key 53 - '/' */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 54 - right shift - */
    {	0x372a, 0xffff, 0x3772, 0xffff }, /* key 55 - prt-scr - */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 56 - Alt - */
    {	0x3920, 0x3920, 0x3920, 0x3920 }, /* key 57 - space bar */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 58 - caps-lock -  */
    {	0x3b00, 0x5400, 0x5e00, 0x6800 }, /* key 59 - F1 */
    {	0x3c00, 0x5500, 0x5f00, 0x6900 }, /* key 60 - F2 */
    {	0x3d00, 0x5600, 0x6000, 0x6a00 }, /* key 61 - F3 */
    {	0x3e00, 0x5700, 0x6100, 0x6b00 }, /* key 62 - F4 */
    {	0x3f00, 0x5800, 0x6200, 0x6c00 }, /* key 63 - F5 */
    {	0x4000, 0x5900, 0x6300, 0x6d00 }, /* key 64 - F6 */
    {	0x4100, 0x5a00, 0x6400, 0x6e00 }, /* key 65 - F7 */
    {	0x4200, 0x5b00, 0x6500, 0x6f00 }, /* key 66 - F8 */
    {	0x4300, 0x5c00, 0x6600, 0x7000 }, /* key 67 - F9 */
    {	0x4400, 0x5d00, 0x6700, 0x7100 }, /* key 68 - F10 */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 69 - num-lock - */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 70 - scroll-lock -  */
    {	0x4700, 0x4737, 0x7700, 0xffff }, /* key 71 - home */
    {	0x4800, 0x4838, 0x8d00, 0x9800 }, /* key 72 - cursor up */
    {	0x4900, 0x4939, 0x8400, 0xffff }, /* key 73 - page up */
    {	0x4a2d, 0x4a2d, 0xffff, 0xffff }, /* key 74 - minus sign */
    {	0x4b00, 0x4b34, 0x7300, 0xffff }, /* key 75 - cursor left */
    {	0xffff, 0x4c35, 0xffff, 0xffff }, /* key 76 - center key */
    {	0x4d00, 0x4d36, 0x7400, 0xffff }, /* key 77 - cursor right */
    {	0x4e2b, 0x4e2b, 0xffff, 0xffff }, /* key 78 - plus sign */
    {	0x4f00, 0x4f31, 0x7500, 0xffff }, /* key 79 - end */
    {	0x5000, 0x5032, 0x9100, 0xa000 }, /* key 80 - cursor down */
    {	0x5100, 0x5133, 0x7600, 0xffff }, /* key 81 - page down */
    {	CIO_KEY_IC, CIO_KEY_SHIFT_IC, CIO_KEY_CTRL_IC, CIO_KEY_ALT_IC}, /* key 82 - insert */
    {	CIO_KEY_DC, CIO_KEY_SHIFT_DC, CIO_KEY_CTRL_DC, CIO_KEY_ALT_IC}, /* key 83 - delete */
deuce's avatar
deuce committed
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 84 - sys key */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 85 */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 86 */
    {	0x8500, 0x5787, 0x8900, 0x8b00 }, /* key 87 - F11 */
    {	0x8600, 0x5888, 0x8a00, 0x8c00 }, /* key 88 - F12 */
};

static struct AtomDef *
get_atom_def(Atom atom)
{
	enum UsedAtom a;

	for (a = 0; a < ATOMCOUNT; a++) {
		if (SupportedAtoms[a].atom == atom)
			return &SupportedAtoms[a];
	}

	return NULL;
}

static void
initialize_atoms(void)
{
	enum UsedAtom a;
	Atom atr;
	int afr;
	unsigned long nir;
	unsigned long bytes_left = 4;
	unsigned char *prop = NULL;
	Window w;
	Atom atom;
	struct AtomDef *ad;
	long offset;

	supported_standards |= (1 << UTF8_ATOM);
	supported_standards |= (1 << ICCM_ATOM);

	for (a = 0; a < ATOMCOUNT; a++) {
		SupportedAtoms[a].atom = x11.XInternAtom(dpy, (char *)SupportedAtoms[a].name, False);
	}

	if (A(UTF8_STRING) == None)
		supported_standards &= ~(1 << UTF8_ATOM);

	if (supported_standards & (1 << UTF8_ATOM)) {
		if (x11.XGetWindowProperty(dpy, root, A(_NET_SUPPORTING_WM_CHECK), 0, 1, False, XA_WINDOW, &atr, &afr, &nir, &bytes_left, &prop) == Success) {
			if (atr == XA_WINDOW) {
				if (nir == 1) {
					w = *(Window *)prop;
					x11.XFree(prop);
					prop = NULL;
					if (x11.XGetWindowProperty(dpy, w, A(_NET_SUPPORTING_WM_CHECK), 0, 1, False, AnyPropertyType, &atr, &afr, &nir, &bytes_left, &prop) == Success) {
						if (atr == XA_WINDOW) {
							if (nir == 1) {
								if (w == *(Window *)prop) {
									supported_standards |= (1 << EWMH_ATOM);
								}
							}
						}
					}
					if (prop != NULL)
						x11.XFree(prop);
					if (x11.XGetWindowProperty(dpy, w, A(_NET_WM_NAME), 0, 0, False, AnyPropertyType, &atr, &afr, &nir, &bytes_left, &prop) == Success) {
						if (prop != NULL)
							x11.XFree(prop);
						if (afr == 8) {
							if (wm_wm_name != NULL)
								free(wm_wm_name);
							wm_wm_name = malloc(bytes_left + 1);
							if (x11.XGetWindowProperty(dpy, w, A(_NET_WM_NAME), 0, bytes_left, False, AnyPropertyType, &atr, &afr, &nir, &bytes_left, &prop) == Success) {
								if (afr == 8) {
									memcpy(wm_wm_name, prop, nir);
									wm_wm_name[nir] = 0;
								}
							}
						}
					}
				}
			}
			if (prop != NULL)
				x11.XFree(prop);
		}
	}

	if (!(supported_standards & (1 << EWMH_ATOM))) {
		for (a = 0; a < ATOMCOUNT; a++) {
			if (SupportedAtoms[a].standard == EWMH_ATOM)
				SupportedAtoms[a].atom = None;
		}
	}
	else {
		for (offset = 0, bytes_left = 1; bytes_left; offset += nir) {
			if (x11.XGetWindowProperty(dpy, root, A(_NET_SUPPORTED), offset, 1, False, XA_ATOM, &atr, &afr, &nir, &bytes_left, &prop) != Success)
				break;
			if (atr != XA_ATOM) {
				x11.XFree(prop);
				break;
			}
			atom = *(Atom *)prop;
			x11.XFree(prop);
			if (atom != None) {
				ad = get_atom_def(atom);
				if (ad != NULL)
					ad->supported = true;
			}
		}
		for (a = 0; a < ATOMCOUNT; a++) {
			if (SupportedAtoms[a].atom != None) {
				if (SupportedAtoms[a].standard != EWMH_ATOM)
					SupportedAtoms[a].supported = true;
				if (!SupportedAtoms[a].supported) {
					SupportedAtoms[a].atom = None;
				}
			}
		}
	}

	for (a = 0; a < ATOMCOUNT; a++) {
		if (SupportedAtoms[a].req_type == XA_UTF8_STRING) {
			if (A(UTF8_STRING) == None) {
				SupportedAtoms[a].supported = false;
				SupportedAtoms[a].atom = None;
			}
			else {
				SupportedAtoms[a].req_type = A(UTF8_STRING);
			}
		}
	}

	/*
	 * ChromeOS doesn't do anything reasonable with PRIMARY...
	 * Hack in use of CLIPBOARD instead.
	 */
	if (wm_wm_name != NULL && strcmp(wm_wm_name, "Sommelier") == 0) {
		if (A(CLIPBOARD) != None)
			copy_paste_selection = A(CLIPBOARD);
	}
}

static bool
set_win_property(enum UsedAtom atom, Atom type, int format, int action, const void *value, int count)
{
	struct AtomDef *ad = &SupportedAtoms[atom];

	if (ad->atom == None)
		return false;
	if (!(supported_standards & (1 << ad->standard)))
		return false;
	return (x11.XChangeProperty(dpy, win, ad->atom, type, format, action, (unsigned char *)value, count) != 0);
}

static bool
fullscreen_geometry(int *x_org, int *y_org, int *width, int *height)
{
	Window root;
	Window cr;
	uint64_t dummy;
	unsigned int rw, rh;
	int wx, wy;
#if defined(WITH_XRANDR) || defined(WITH_XINERAMA)
	int cx, cy;
#endif
#ifdef WITH_XRANDR
	static XRRScreenResources *xrrsr = NULL;
	XRRCrtcInfo *xrrci = NULL;
	int searched = 0;
	bool found;
#endif
#ifdef WITH_XINERAMA
	int i;
	int nscrn;
	XineramaScreenInfo *xsi;
#endif

	if (win == 0)
		return false;

	if (x11.XGetGeometry(dpy, win, (void *)&root, &wx, &wy, &rw, &rh, (void *)&dummy, (void *)&dummy) == 0)
		return false;

#if defined(WITH_XRANDR) || defined(WITH_XINERAMA)
	x11.XTranslateCoordinates(dpy, win, root, wx, wy, &cx, &cy, &cr);
	cx += rw / 2;
	cy += rh / 2;
#endif

#ifdef WITH_XRANDR
	// We likely don't actually need Ximerama... this is always better and almost certainly present
	if (xrandr_found) {
		searched = 0;
		found = false;
		while (searched < 10 && found == false) {
			searched++;
			if (xrrsr == NULL)
				xrrsr = x11.XRRGetScreenResources(dpy, root);
			if (xrrsr == NULL)
				break;
			for (i = 0; i < xrrsr->ncrtc; i++) {
				if (xrrci != NULL)
					x11.XRRFreeCrtcInfo(xrrci);
				xrrci = x11.XRRGetCrtcInfo(dpy, xrrsr, xrrsr->crtcs[i]);
				if (xrrci == NULL) {
					x11.XRRFreeScreenResources(xrrsr);
					xrrsr = NULL;
					break;
				}
				if (cx >= xrrci->x && cx < xrrci->x + xrrci->width
				    && cy >= xrrci->y && cy < xrrci->y + xrrci->height) {
					found = true;
					break;
				}
			}
		}
		if (xrrsr != NULL && !found) {
			if (xrrci != NULL)
				x11.XRRFreeCrtcInfo(xrrci);
			xrrci = x11.XRRGetCrtcInfo(dpy, xrrsr, xrrsr->crtcs[0]);
			if (xrrci != NULL)
				found = true;
		}
		if (found) {
			if (x_org)
				*x_org = xrrci->x;
			if (y_org)
				*y_org = xrrci->y;
			if (width)
				*width = xrrci->width;
			if (height)
				*height = xrrci->height;
			if (xrrci != NULL)
				x11.XRRFreeCrtcInfo(xrrci);
			return true;
		}
		if (xrrci != NULL)
			x11.XRRFreeCrtcInfo(xrrci);
	}
#endif
#ifdef WITH_XINERAMA
	if (xinerama_found) {
		// NOTE: Xinerama is limited to a short for the entire screen dimensions.
		if ((xsi = x11.XineramaQueryScreens(dpy, &nscrn)) != NULL && nscrn != 0) {
			for (i = 0; i < nscrn; i++) {
				if (cx >= xsi[i].x_org && cx < xsi[i].x_org + xsi[i].width
				    && cy >= xsi[i].y_org && cy < xsi[i].y_org + xsi[i].height) {
					break;
				}
			}
			if (i == nscrn)
				i = 0;
			if (x_org)
				*x_org = xsi[i].x_org;
			if (y_org)
				*y_org = xsi[i].y_org;
			if (width)
				*width = xsi[i].width;
			if (height)
				*height = xsi[i].height;
			x11.XFree(xsi);
			return true;
		}
	}
#endif
	if (x_org)
		*x_org = 0;
	if (y_org)
		*y_org = 0;
	if (width)
		*width = rw;
	if (height)
		*height = rh;
	return true;
}

Deucе's avatar
Deucе committed
static bool
x11_get_maxsize(int *w, int *h)
{
	int i;
	long offset;
	int maxw = 0, maxh = 0;
	Atom atr;
	int afr;
	long *ret;
	unsigned long nir;
	unsigned long bytes_left;
Deucе's avatar
Deucе committed
	unsigned char *prop;
	long desktop = -1;
Deucе's avatar
Deucе committed

	if (dpy == NULL)
		return false;
		// TODO: We may not need this if we can wait for ConfigurNotify on fullscreen...
		return fullscreen_geometry(NULL, NULL, w, h);
Deucе's avatar
Deucе committed
	}
	else {
		// First, try to get _NET_WORKAREA...
		if (A(_NET_WM_DESKTOP) != None && win != 0) {
			if (x11.XGetWindowProperty(dpy, win, A(_NET_WM_DESKTOP), 0, 1, False, XA_CARDINAL, &atr, &afr, &nir, &bytes_left, &prop) == Success) {
				if (atr == XA_CARDINAL && afr == 32 && nir > 0) {
					desktop = *(long *)prop;
				}
				x11.XFree(prop);
			}
		if (A(_NET_WORKAREA) != None) {
			for (i = 0, offset = 0, bytes_left = 1; bytes_left; i++, offset += nir) {
				if (x11.XGetWindowProperty(dpy, root, A(_NET_WORKAREA), offset, 4, False, XA_CARDINAL, &atr, &afr, &nir, &bytes_left, &prop) != Success)
				if (atr != XA_CARDINAL) {
					x11.XFree(prop);
				ret = (long *)prop;
				if (desktop == -1) {
					if (nir >= 3) {
						if (ret[2] > maxw)
							maxw = ret[2];
					}
					if (nir >= 4) {
						if (ret[3] > maxh)
							maxh = ret[3];
					}
				else if (desktop == i) {
					if (nir >= 3)
						maxw = ret[2];
					if (nir >= 4)
					x11.XFree(prop);
					break;
Deucе's avatar
Deucе committed
			}
		}
		if (maxw > 0 && maxh > 0) {
			if (A(_NET_FRAME_EXTENTS) != None && win != 0) {
				if (x11.XGetWindowProperty(dpy, win, A(_NET_FRAME_EXTENTS), 0, 4, False, XA_CARDINAL, &atr, &afr, &nir, &bytes_left, &prop) == Success) {
					if (atr == XA_CARDINAL && afr == 32 && nir == 4) {
						ret = (long *)prop;
						maxw -= ret[0] + ret[1];
						maxh -= ret[2] + ret[3];
					}
					x11.XFree(prop);
				}
			}
		}
	// Couldn't get work area, get size of screen instead. :(
Deucе's avatar
Deucе committed
	if (maxw == 0 || maxh == 0) {
		// We could just use root window size here...
		fullscreen_geometry(NULL, NULL, &maxw, &maxh);
Deucе's avatar
Deucе committed
	}
Deucе's avatar
Deucе committed
	if (maxw != 0 && maxh != 0) {
		*w = maxw;
		*h = maxh;
		return true;
	}
	return false;
}

Deucе's avatar
Deucе committed
static void
resize_pictures(void)
deuce's avatar
deuce committed
{
Deucе's avatar
Deucе committed
#ifdef WITH_XRENDER
	if (xrender_found) {
		if (xrender_pf == NULL)
			xrender_pf = x11.XRenderFindStandardFormat(dpy, PictStandardRGB24);
		if (xrender_pf == NULL)
			xrender_pf = x11.XRenderFindVisualFormat(dpy, visual);
		if (xrender_pf == NULL)
			xrender_pf = x11.XRenderFindVisualFormat(dpy, DefaultVisual(dpy, DefaultScreen(dpy)));

		if (xrender_pm != None)
			x11.XFreePixmap(dpy, xrender_pm);
		xrender_pm = x11.XCreatePixmap(dpy, win, x_cvstat.scrnwidth, x_cvstat.scrnheight, depth);

		if (xrender_src_pict != None)
			x11.XRenderFreePicture(dpy, xrender_src_pict);
		if (xrender_dst_pict != None)
			x11.XRenderFreePicture(dpy, xrender_dst_pict);
		XRenderPictureAttributes pa;
		xrender_src_pict = x11.XRenderCreatePicture(dpy, xrender_pm, xrender_pf, 0, &pa);
		xrender_dst_pict = x11.XRenderCreatePicture(dpy, win, xrender_pf, 0, &pa);
		x11.XRenderSetPictureFilter(dpy, xrender_src_pict, "best", NULL, 0);
		pthread_mutex_lock(&vstatlock);
		bitmap_get_scaled_win_size(vstat.scaling, &iw, &ih, 0, 0);
		pthread_mutex_unlock(&vstatlock);
		XTransform transform_matrix = {{
		  {XDoubleToFixed((double)vstat.scrnwidth / iw), XDoubleToFixed(0), XDoubleToFixed(0)},
		  {XDoubleToFixed(0), XDoubleToFixed((double)vstat.scrnheight / ih), XDoubleToFixed(0)},
		  {XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1.0)}
		}};
		x11.XRenderSetPictureTransform(dpy, xrender_src_pict, &transform_matrix);
	}
Deucе's avatar
Deucе committed
#endif
}

static void resize_xim(void)
{
	int width, height;

	resize_pictures();
	if (x_internal_scaling) {
		pthread_mutex_lock(&vstatlock);
		bitmap_get_scaled_win_size(x_cvstat.scaling, &width, &height, 0, 0);
		pthread_mutex_unlock(&vstatlock);
	}
	else {
		width = x_cvstat.scrnwidth;
		height = x_cvstat.scrnheight;
	}
deuce's avatar
deuce committed
	if (xim) {
		if (width == xim->width
		    && height == xim->height) {
			if (last) {
				release_buffer(last);
				last = NULL;
			}
deuce's avatar
deuce committed
#ifdef XDestroyImage
		XDestroyImage(xim);
#else
		x11.XDestroyImage(xim);
#endif
	}
		release_buffer(last);
Deucе's avatar
Deucе committed
	xim = x11.XCreateImage(dpy, visual, depth, ZPixmap, 0, NULL, width, height, 32, 0);
	xim->data=(char *)calloc(1, xim->bytes_per_line*xim->height);
/* Swiped from FreeBSD libc */
static int
my_fls(unsigned long mask)
{
        int bit;

        if (mask == 0)
                return (0);
        for (bit = 1; mask != 1; bit++)
                mask = mask >> 1;
        return (bit);
}

Deucе's avatar
Deucе committed
/*
 * Actually maps (shows) the window
 */
static void map_window()
{
	XSizeHints *sh;

	sh = x11.XAllocSizeHints();
	if (sh == NULL) {
		fprintf(stderr, "Could not get XSizeHints structure");
		exit(1);
	}

	if (x11_get_maxsize(&sh->max_width,&sh->max_height)) {
Deucе's avatar
Deucе committed
		pthread_mutex_lock(&vstatlock);
		if (!fullscreen)
			bitmap_get_scaled_win_size(bitmap_double_mult_inside(sh->max_width, sh->max_height), &sh->max_width, &sh->max_height, sh->max_width, sh->max_height);
	}
	else {
		pthread_mutex_lock(&vstatlock);
		bitmap_get_scaled_win_size(7.0, &sh->max_width, &sh->max_height, 0, 0);
	bitmap_get_scaled_win_size(x_cvstat.scaling, &sh->base_width, &sh->base_height, 0, 0);
	sh->width = sh->base_width;
	sh->height = sh->base_height;
	bitmap_get_scaled_win_size(1.0, &sh->min_width, &sh->min_height, 0, 0);
	pthread_mutex_unlock(&vstatlock);
Deucе's avatar
Deucе committed

	if (x_cvstat.aspect_width != 0 && x_cvstat.aspect_height != 0) {
		sh->min_aspect.x = sh->max_aspect.x = x_cvstat.aspect_width;
		sh->min_aspect.y = sh->max_aspect.y = x_cvstat.aspect_height;
	}
	else {
		sh->min_aspect.x = sh->max_aspect.x = sh->min_width;
		sh->min_aspect.y = sh->max_aspect.y = sh->min_height;
	}
Deucе's avatar
Deucе committed

	sh->flags = PMinSize | PSize | PAspect | PMaxSize | PBaseSize;
Deucе's avatar
Deucе committed

	x11.XSetWMNormalHints(dpy, win, sh);
	pthread_mutex_lock(&vstatlock);
	vstat.scaling = x_cvstat.scaling;
	pthread_mutex_unlock(&vstatlock);
	if (map_pending)
		x11.XMapWindow(dpy, win);
Deucе's avatar
Deucе committed

	x11.XFree(sh);

	return;
}

Deucе's avatar
Deucе committed
static void
set_icon(const void *data, size_t width, XWMHints *hints)
{
	const uint32_t *idata = data;
	Pixmap opm = icn;
	Pixmap opmm = icn_mask;
	XGCValues gcv = {
		.function = GXcopy,
		.foreground = black | 0xff000000,
		.background = white
	};
	int x,y,i;
	GC igc, imgc;
	bool fail = false;
	unsigned short tmp;
	XColor fg;
	bool sethints = (hints == NULL);
	unsigned long lasti = ULONG_MAX, lastim = ULONG_MAX;
	// This is literally the wost possible way to create a pixmap. :)
	/*
	 * This whole mess was added to get the icon working on ChromeOS...
	 * as it happens, ChromeOS doesn't actually use the X11 icons at
	 * all for anything ever.  Instead it does some weird hackery in
	 * the icon theme and pulls the icon files out to the host.
	 * Leaving this here though since it is marginally "better" aside
	 * from the insane method to create a Pixmap.
	 */
	icn = x11.XCreatePixmap(dpy, root, width, width, depth);
Deucе's avatar
Deucе committed
	igc = x11.XCreateGC(dpy, icn, GCFunction | GCForeground | GCBackground | GCGraphicsExposures, &gcv);
	icn_mask = x11.XCreatePixmap(dpy, root, width, width, 1);
Deucе's avatar
Deucе committed
	imgc = x11.XCreateGC(dpy, icn_mask, GCFunction | GCForeground | GCBackground | GCGraphicsExposures, &gcv);
	fail = (!icn) || (!icn_mask);
	if (!fail) {
		for (x = 0, i = 0; x < width; x++) {
			for (y = 0; y < width; y++) {
				if (idata[i] & 0xff000000) {
					if (VisualIsRGB8)
						fg.pixel = idata[i] & 0xffffff;
Deucе's avatar
Deucе committed
					else {
						tmp = (idata[i] & 0xff);
						fg.red = tmp << 8 | tmp;
						tmp = (idata[i] & 0xff00) >> 8;
						fg.green = tmp << 8 | tmp;
						tmp = (idata[i] & 0xff0000) >> 16;
						fg.blue = tmp << 8 | tmp;
						fg.flags = DoRed | DoGreen | DoBlue;
						if (x11.XAllocColor(dpy, wincmap, &fg) == 0)
							fail = true;
Deucе's avatar
Deucе committed
					}
					if (fail)
						break;
					if (lasti != fg.pixel) {
						x11.XSetForeground(dpy, igc, fg.pixel);
						lasti = fg.pixel;
					}
					x11.XDrawPoint(dpy, icn, igc, y, x);
					if (lastim != white) {
						x11.XSetForeground(dpy, imgc, white);
						lastim = white;
					}
					x11.XDrawPoint(dpy, icn_mask, imgc, y, x);
Deucе's avatar
Deucе committed
				}
				else {
					if (lastim != black) {
						x11.XSetForeground(dpy, imgc, black);
						lastim = black;
					}
Deucе's avatar
Deucе committed
					x11.XDrawPoint(dpy, icn_mask, imgc, y, x);
				}
				i++;
			}
			if (fail)
				break;
		}
	}
	if (!fail) {
		if (!hints)
			hints = x11.XGetWMHints(dpy, win);
		if (!hints)
			hints = x11.XAllocWMHints();
		if (hints) {
			hints->flags |= IconPixmapHint | IconMaskHint;
			hints->icon_pixmap = icn;
			hints->icon_mask = icn_mask;
			if (sethints) {
				x11.XSetWMHints(dpy, win, hints);
				x11.XFree(hints);
			}
			if (opm)
				x11.XFreePixmap(dpy, opm);
			if (opmm)
				x11.XFreePixmap(dpy, opmm);
		}
	}
	x11.XFreeGC(dpy, igc);
	x11.XFreeGC(dpy, imgc);
}

deuce's avatar
deuce committed
/* Get a connection to the X server and create the window. */
static int init_window()
{
	XClassHint *classhints;
Deucе's avatar
Deucе committed
	int w, h;
	int mw, mh;
Deucе's avatar
Deucе committed
	int screen;
	int major, minor;
	unsigned long pid_l;
	Atom a;
deuce's avatar
deuce committed

	dpy = x11.XOpenDisplay(NULL);
deuce's avatar
deuce committed
		return(-1);
	}
	root = DefaultRootWindow(dpy);
	x11.XSynchronize(dpy, False);
Deucе's avatar
Deucе committed

#ifdef WITH_XRENDER
	if (xrender_found && x11.XRenderQueryVersion(dpy, &major, &minor) == 0)
		xrender_found = false;
#endif
#ifdef WITH_XINERAMA
	if (xinerama_found && x11.XineramaQueryVersion(dpy, &major, &minor) == 0)
		xinerama_found = false;
#endif
#ifdef WITH_XRANDR
	if (xrandr_found && x11.XRRQueryVersion(dpy, &major, &minor) == 0)
		xrandr_found = false;
#endif
Deucе's avatar
Deucе committed

	xfd = ConnectionNumber(dpy);
	initialize_atoms();
deuce's avatar
deuce committed

Deucе's avatar
Deucе committed
	screen = DefaultScreen(dpy);
#ifdef DefaultVisual
	visual = DefaultVisual(dpy, screen);
#else
	visual = x11.DefaultVisual(dpy, screen);
#endif
#ifdef DefaultDepth
	depth = DefaultDepth(dpy, screen);
#else
	depth = x11.DefaultDepth(dpy, screen);
#endif
	base_pixel = ULONG_MAX;
	base_pixel &= ~visual->red_mask;
	base_pixel &= ~visual->green_mask;
	base_pixel &= ~visual->blue_mask;
	r_shift = my_fls(visual->red_mask)-16;
	g_shift = my_fls(visual->green_mask)-16;
	b_shift = my_fls(visual->blue_mask)-16;
	if (visual->red_mask == 0xff0000 && visual->green_mask == 0xff00 && visual->blue_mask == 0xff)
		VisualIsRGB8 = true;
deuce's avatar
deuce committed
	/* Allocate black and white */
	black=BlackPixel(dpy, DefaultScreen(dpy));
	white=WhitePixel(dpy, DefaultScreen(dpy));

	/* Create window, but defer setting a size and GC. */
	XSetWindowAttributes wa = {0};
	parent = root;
	wincmap = x11.XCreateColormap(dpy, root, visual, AllocNone);
Deucе's avatar
Deucе committed
	x11.XInstallColormap(dpy, wincmap);
	wa.colormap = wincmap;
	wa.background_pixel = black;
	wa.border_pixel = black;
Deucе's avatar
Deucе committed
	x11_get_maxsize(&mw, &mh);
	pthread_mutex_lock(&vstatlock);
	bitmap_get_scaled_win_size(x_cvstat.scaling, &w, &h, mw, mh);
	vstat.winwidth = x_cvstat.winwidth = w;
	vstat.winheight = x_cvstat.winheight = h;
Deucе's avatar
Deucе committed
	vstat.scaling = x_cvstat.scaling;
	pthread_mutex_unlock(&vstatlock);
	win = x11.XCreateWindow(dpy, parent, 0, 0,
Deucе's avatar
Deucе committed
	    w, h, 2, depth, InputOutput, visual, CWColormap | CWBorderPixel | CWBackPixel, &wa);
deuce's avatar
deuce committed

	classhints=x11.XAllocClassHint();
	if (classhints) {
Deucе's avatar
Deucе committed
		classhints->res_name = (char *)ciolib_initial_program_name;
		classhints->res_class = (char *)ciolib_initial_program_class;
	wmhints=x11.XAllocWMHints();
	if(wmhints) {
		wmhints->initial_state=NormalState;
Deucе's avatar
Deucе committed
		wmhints->flags |= (StateHint | InputHint);
Deucе's avatar
Deucе committed
		set_icon(ciolib_initial_icon, ciolib_initial_icon_width, wmhints);
		x11.XSetWMProperties(dpy, win, NULL, NULL, 0, 0, NULL, wmhints, classhints);
		x11.XFree(wmhints);
	set_win_property(ATOM__NET_WM_ICON, XA_CARDINAL, 32, PropModeReplace, ciolib_initial_icon, ciolib_initial_icon_width * ciolib_initial_icon_width);
Deucе's avatar
Deucе committed

	pid_l = getpid();
	set_win_property(ATOM__NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, &pid_l, 1);
	a = A(_NET_WM_WINDOW_TYPE_NORMAL);
	if (a != None)
		set_win_property(ATOM__NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, &a, 1);
	a = A(_NET_WM_STATE_FULLSCREEN);
	if (a != None)