Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

x_events.c 35.9 KB
Newer Older
deuce's avatar
deuce committed
1 2 3 4 5
/*
 * This file contains ONLY the functions that are called from the
 * event thread.
 */
 
Deucе's avatar
Deucе committed
6
#include <math.h>
deuce's avatar
deuce committed
7
#include <unistd.h>
deuce's avatar
deuce committed
8
#include <stdbool.h>
deuce's avatar
deuce committed
9 10 11

#include <fcntl.h>
#include <limits.h>
deuce's avatar
deuce committed
12
#include <locale.h>
deuce's avatar
deuce committed
13 14 15 16 17 18
#include <stdio.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
19
#include <X11/cursorfont.h>
deuce's avatar
deuce committed
20 21 22 23 24 25 26

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

#include "vidmodes.h"

27
#include "ciolib.h"
28
#define BITMAP_CIOLIB_DRIVER
deuce's avatar
deuce committed
29 30
#include "bitmap_con.h"
#include "link_list.h"
Deucе's avatar
Deucе committed
31
#include "scale.h"
deuce's avatar
deuce committed
32 33
#include "x_events.h"
#include "x_cio.h"
34
#include "utf8_codepages.h"
deuce's avatar
deuce committed
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

/*
 * 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_window_width;
int x11_window_height;
56
int x11_initialized=0;
57 58
sem_t	event_thread_complete;
int	terminate = 0;
59 60 61
Atom	copybuf_format;
Atom	pastebuf_format;

deuce's avatar
deuce committed
62 63 64 65 66 67
/*
 * Local variables
 */

/* Sets the atom to be used for copy/paste operations */
#define CONSOLE_CLIPBOARD	XA_PRIMARY
deuce's avatar
deuce committed
68
static Atom WM_DELETE_WINDOW=0;
deuce's avatar
deuce committed
69 70 71

static Display *dpy=NULL;
static Window win;
72
static Cursor curs = None;
deuce's avatar
deuce committed
73
static Visual visual;
74
static bool VisualIsRGB8 = false;
deuce's avatar
deuce committed
75
static XImage *xim;
deuce's avatar
deuce committed
76 77
static XIM im;
static XIC ic;
deuce's avatar
deuce committed
78 79 80 81
static unsigned int depth=0;
static int xfd;
static unsigned long black;
static unsigned long white;
82 83
static int bitmap_width=0;
static int bitmap_height=0;
84
static int old_scaling = 0;
85
struct video_stats x_cvstat;
86 87 88 89
static unsigned long base_pixel;
static int r_shift;
static int g_shift;
static int b_shift;
Deucе's avatar
Deucе committed
90
static struct graphics_buffer *last = NULL;
91

deuce's avatar
deuce committed
92
/* Array of Graphics Contexts */
93
static GC gc;
deuce's avatar
deuce committed
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201

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 */
202 203
    {	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
204 205 206 207 208 209 210
    {	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 */
};

deuce's avatar
deuce committed
211 212
static void resize_xim(void)
{
Deucе's avatar
Deucе committed
213
	int width = bitmap_width * x_cvstat.scaling;
214
	int height = bitmap_height * x_cvstat.scaling;
Deucе's avatar
Deucе committed
215

216
	aspect_correct(&width, &height, x_cvstat.aspect_width, x_cvstat.aspect_height);
deuce's avatar
deuce committed
217
	if (xim) {
Deucе's avatar
Deucе committed
218 219
		if (width == xim->width
		    && height == xim->height) {
220 221 222 223
			if (last) {
				release_buffer(last);
				last = NULL;
			}
224
			x11.XFillRectangle(dpy, win, gc, 0, 0, x11_window_width, x11_window_height);
225 226
			return;
		}
deuce's avatar
deuce committed
227 228 229 230 231 232
#ifdef XDestroyImage
		XDestroyImage(xim);
#else
		x11.XDestroyImage(xim);
#endif
	}
233
	if (last) {
Deucе's avatar
Deucе committed
234
		release_buffer(last);
235 236
		last = NULL;
	}
Deucе's avatar
Deucе committed
237 238
	xim = x11.XCreateImage(dpy, &visual, depth, ZPixmap, 0, NULL, width, height, 32, 0);
	xim->data=(char *)calloc(1, xim->bytes_per_line*xim->height);
239
	x11.XFillRectangle(dpy, win, gc, 0, 0, x11_window_width, x11_window_height);
deuce's avatar
deuce committed
240 241
}

242 243 244 245 246 247 248 249 250 251 252 253 254
/* 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);
}

deuce's avatar
deuce committed
255 256 257
/* Get a connection to the X server and create the window. */
static int init_window()
{
258 259
	XGCValues gcv;
	int i;
260
	XWMHints *wmhints;
deuce's avatar
deuce committed
261
	XClassHint *classhints;
262 263 264 265 266 267
	int ret;
	int best=-1;
	int best_depth=0;
	int best_cmap=0;
	XVisualInfo template = {0};
	XVisualInfo *vi;
deuce's avatar
deuce committed
268 269

	dpy = x11.XOpenDisplay(NULL);
270
	if (dpy == NULL) {
deuce's avatar
deuce committed
271 272
		return(-1);
	}
273 274
	xfd = ConnectionNumber(dpy);
	x11.utf8 = x11.XInternAtom(dpy, "UTF8_STRING", False);
275
	x11.targets = x11.XInternAtom(dpy, "TARGETS", False);
deuce's avatar
deuce committed
276

277 278 279 280 281 282 283 284 285 286 287
	template.screen = DefaultScreen(dpy);
	template.class = TrueColor;
	vi = x11.XGetVisualInfo(dpy, VisualScreenMask | VisualClassMask, &template, &ret);
	for (i=0; i<ret; i++) {
		if (vi[i].depth >= best_depth && vi[i].colormap_size >= best_cmap) {
			best = i;
			best_depth = vi[i].depth;
		}
	}
	if (best != -1) {
		visual = *vi[best].visual;
288 289 290 291 292 293 294
		/*
		 * TODO: Set VisualIsRGB8 if appropriate...
		 * "appropriate" in this context means it's a sequence of
		 * unpadded uint32_t values in XXRRGGBB format where XX is
		 * ignored, and RR, GG, and BB are Red, Green, Blue values
		 * respectively.
		 */
295 296 297 298
		base_pixel = ULONG_MAX;
		base_pixel &= ~visual.red_mask;
		base_pixel &= ~visual.green_mask;
		base_pixel &= ~visual.blue_mask;
299 300 301
		r_shift = my_fls(visual.red_mask)-16;
		g_shift = my_fls(visual.green_mask)-16;
		b_shift = my_fls(visual.blue_mask)-16;
302 303 304 305 306 307 308 309
	}
	else {
		fprintf(stderr, "Unable to find TrueColor visual\n");
		x11.XFree(vi);
		return -1;
	}
	x11.XFree(vi);

deuce's avatar
deuce committed
310 311 312 313 314
	/* Allocate black and white */
	black=BlackPixel(dpy, DefaultScreen(dpy));
	white=WhitePixel(dpy, DefaultScreen(dpy));

    /* Create window, but defer setting a size and GC. */
315 316 317 318 319 320
	XSetWindowAttributes wa = {0};
	wa.colormap = x11.XCreateColormap(dpy, DefaultRootWindow(dpy), &visual, AllocNone);
	wa.background_pixel = black;
	wa.border_pixel = black;
	depth = best_depth;
    win = x11.XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0,
321
			      640*x_cvstat.scaling, 400*x_cvstat.scaling, 2, depth, InputOutput, &visual, CWColormap | CWBorderPixel | CWBackPixel, &wa);
deuce's avatar
deuce committed
322

deuce's avatar
deuce committed
323 324 325
	classhints=x11.XAllocClassHint();
	if (classhints)
		classhints->res_name = classhints->res_class = "CIOLIB";
326 327 328
	wmhints=x11.XAllocWMHints();
	if(wmhints) {
		wmhints->initial_state=NormalState;
deuce's avatar
deuce committed
329
		wmhints->flags = (StateHint/* | IconPixmapHint | IconMaskHint*/ | InputHint);
330
		wmhints->input = True;
deuce's avatar
deuce committed
331 332
		x11.XSetWMProperties(dpy, win, NULL, NULL, 0, 0, NULL, wmhints, classhints);
		x11.XFree(wmhints);
333
	}
deuce's avatar
deuce committed
334 335 336 337 338 339 340
	im = x11.XOpenIM(dpy, NULL, "CIOLIB", "CIOLIB");
	if (im != NULL) {
		ic = x11.XCreateIC(im, XNClientWindow, win, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, NULL);
		if (ic)
			x11.XSetICFocus(ic);
	}

deuce's avatar
deuce committed
341 342
	if (classhints)
		x11.XFree(classhints);
343

deuce's avatar
deuce committed
344 345
	WM_DELETE_WINDOW = x11.XInternAtom(dpy, "WM_DELETE_WINDOW", False);

deuce's avatar
deuce committed
346
	gcv.function = GXcopy;
347 348
	gcv.foreground = black | 0xff000000;
	gcv.background = white;
deuce's avatar
deuce committed
349
	gcv.graphics_exposures = False;
350
	gc=x11.XCreateGC(dpy, win, GCFunction | GCForeground | GCBackground | GCGraphicsExposures, &gcv);
deuce's avatar
deuce committed
351

352
	x11.XSelectInput(dpy, win, KeyReleaseMask | KeyPressMask |
deuce's avatar
deuce committed
353 354 355
		     ExposureMask | ButtonPressMask
		     | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask);

356
	x11.XStoreName(dpy, win, "SyncConsole");
deuce's avatar
deuce committed
357
	x11.XSetWMProtocols(dpy, win, &WM_DELETE_WINDOW, 1);
deuce's avatar
deuce committed
358 359 360 361

	return(0);
}

362 363 364 365
/*
 * Actually maps (shows) the window
 */
static void map_window()
deuce's avatar
deuce committed
366
{
367 368 369
	XSizeHints *sh;
	int minwidth = bitmap_width;
	int minheight = bitmap_height;
deuce's avatar
deuce committed
370

371 372
	sh = x11.XAllocSizeHints();
	if (sh == NULL) {
deuce's avatar
deuce committed
373 374 375 376
		fprintf(stderr, "Could not get XSizeHints structure");
		exit(1);
	}

377 378
	sh->base_width = bitmap_width * x_cvstat.scaling;
	sh->base_height = bitmap_height * x_cvstat.scaling;
deuce's avatar
deuce committed
379

380 381
	aspect_correct(&sh->base_width, &sh->base_height, x_cvstat.aspect_width, x_cvstat.aspect_height);
	aspect_correct(&minwidth, &minheight, x_cvstat.aspect_width, x_cvstat.aspect_height);
Deucе's avatar
Deucе committed
382

383 384
	sh->min_width = sh->width_inc = sh->min_aspect.x = sh->max_aspect.x = minwidth;
	sh->min_height = sh->height_inc = sh->min_aspect.y = sh->max_aspect.y = minheight;
deuce's avatar
deuce committed
385

386
	sh->flags = USSize | PMinSize | PSize | PResizeInc | PAspect;
deuce's avatar
deuce committed
387

388 389
	x11.XSetWMNormalHints(dpy, win, sh);
	x11.XMapWindow(dpy, win);
deuce's avatar
deuce committed
390

391 392 393
	x11.XFree(sh);

	return;
deuce's avatar
deuce committed
394 395
}

396 397 398
/* Resize the window. This function is called after a mode change. */
static void resize_window()
{
399 400 401 402 403
	int width = bitmap_width * x_cvstat.scaling;
	int height = bitmap_height * x_cvstat.scaling;

	aspect_correct(&width, &height, x_cvstat.aspect_width, x_cvstat.aspect_height);
	x11.XResizeWindow(dpy, win, width, height);
Deucе's avatar
Deucе committed
404
	resize_xim();
deuce's avatar
deuce committed
405

Deucе's avatar
Deucе committed
406
	return;
407 408
}

409
static void init_mode_internal(int mode)
deuce's avatar
deuce committed
410
{
411
	int oldcols;
deuce's avatar
deuce committed
412

413
	oldcols=x_cvstat.cols;
414

415
	pthread_mutex_lock(&blinker_lock);
416
	pthread_mutex_lock(&vstatlock);
417
	if (last) {
Deucе's avatar
Deucе committed
418
		release_buffer(last);
419 420
		last = NULL;
	}
421
	bitmap_drv_init_mode(mode, &bitmap_width, &bitmap_height);
deuce's avatar
deuce committed
422

deuce's avatar
deuce committed
423
	/* Deal with 40 col doubling */
deuce's avatar
deuce committed
424
	if(oldcols != vstat.cols) {
deuce's avatar
deuce committed
425
		if(oldcols == 40)
deuce's avatar
deuce committed
426 427 428
			vstat.scaling /= 2;
		if(vstat.cols == 40)
			vstat.scaling *= 2;
deuce's avatar
deuce committed
429
	}
deuce's avatar
deuce committed
430 431
	if(vstat.scaling < 1)
		vstat.scaling = 1;
deuce's avatar
deuce committed
432

deuce's avatar
deuce committed
433 434
	x_cvstat = vstat;
	pthread_mutex_unlock(&vstatlock);
deuce's avatar
deuce committed
435
	pthread_mutex_unlock(&blinker_lock);
436
	map_window();
437 438 439 440 441
}

static void check_scaling(void)
{
	if (old_scaling != x_cvstat.scaling) {
442
		resize_window();
443 444 445 446 447 448 449
		old_scaling = x_cvstat.scaling;
	}
}

static int init_mode(int mode)
{
	init_mode_internal(mode);
450
	resize_window();
451
	bitmap_drv_request_pixels();
deuce's avatar
deuce committed
452 453

	sem_post(&mode_set);
454
	return(0);
deuce's avatar
deuce committed
455 456 457 458 459 460 461
}

static int video_init()
{
    /* If we are running under X, get a connection to the X server and create
       an empty window of size (1, 1). It makes a couple of init functions a
       lot easier. */
462 463
	if(x_cvstat.scaling<1)
		x_setscaling(1);
deuce's avatar
deuce committed
464 465 466
    if(init_window())
		return(-1);

467
	bitmap_drv_init(x11_drawrect, x11_flush);
deuce's avatar
deuce committed
468 469

    /* Initialize mode 3 (text, 80x25, 16 colors) */
470
    init_mode_internal(3);
deuce's avatar
deuce committed
471 472 473 474

    return(0);
}

Deucе's avatar
Deucе committed
475 476
static void
local_draw_rect(struct rectlist *rect)
deuce's avatar
deuce committed
477
{
478
	int x, y, xoff = 0, yoff = 0;
479 480
	unsigned int r, g, b;
	unsigned long pixel;
Deucе's avatar
Deucе committed
481 482 483
	int cleft;
	int cright = -100;
	int ctop;
484
	int cbottom = -1;
485
	int idx;
Deucе's avatar
Deucе committed
486
	uint32_t last_pixel = 0x55555555;
Deucе's avatar
Deucе committed
487
	struct graphics_buffer *source;
deuce's avatar
deuce committed
488

Deucе's avatar
Deucе committed
489 490
	if (bitmap_width != rect->rect.width || bitmap_height != rect->rect.height) {
		bitmap_drv_free_rect(rect);
491
		return;
Deucе's avatar
Deucе committed
492
	}
493 494 495 496 497 498 499 500

	xoff = (x11_window_width - xim->width) / 2;
	if (xoff < 0)
		xoff = 0;
	yoff = (x11_window_height - xim->height) / 2;
	if (yoff < 0)
		yoff = 0;

Deucе's avatar
Deucе committed
501
	// Scale...
502
	source = do_scale(rect, x_cvstat.scaling, x_cvstat.scaling, x_cvstat.aspect_width, x_cvstat.aspect_height);
Deucе's avatar
Deucе committed
503 504
	bitmap_drv_free_rect(rect);
	if (source == NULL)
Deucе's avatar
Deucе committed
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
		return;
	cleft = source->w;
	ctop = source->h;

	xoff = (x11_window_width - source->w) / 2;
	if (xoff < 0)
		xoff = 0;
	yoff = (x11_window_height - source->h) / 2;
	if (yoff < 0)
		yoff = 0;

	if (last && (last->w != source->w || last->h != source->h)) {
		release_buffer(last);
		last = NULL;
	}
Deucе's avatar
Deucе committed
520

521
	/* TODO: Translate into local colour depth */
Deucе's avatar
Deucе committed
522
	idx = 0;
Deucе's avatar
Deucе committed
523

Deucе's avatar
Deucе committed
524 525
	for (y = 0; y < source->h; y++) {
		for (x = 0; x < source->w; x++) {
526
			if (last) {
Deucе's avatar
Deucе committed
527
				if (last->data[idx] != source->data[idx]) {
528 529 530 531
					if (x < cleft)
						cleft = x;
					if (x > cright)
						cright = x;
532 533 534 535
					if (y < ctop)
						ctop = y;
					if (y > cbottom)
						cbottom = y;
536 537
				}
				else {
Deucе's avatar
Deucе committed
538 539
					idx++;
					continue;
540 541
				}
			}
542
			if (VisualIsRGB8) {
Deucе's avatar
Deucе committed
543 544
				pixel = source->data[idx];
				((uint32_t*)xim->data)[idx] = pixel;
Deucе's avatar
Deucе committed
545
			}
Deucе's avatar
Deucе committed
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
			else {
				if (last_pixel != source->data[idx]) {
					last_pixel = source->data[idx];
					r = source->data[idx] >> 16 & 0xff;
					g = source->data[idx] >> 8 & 0xff;
					b = source->data[idx] & 0xff;
					r = (r<<8)|r;
					g = (g<<8)|g;
					b = (b<<8)|b;
					pixel = base_pixel;
					if (r_shift >= 0)
						pixel |= (r << r_shift) & visual.red_mask;
					else
						pixel |= (r >> (0-r_shift)) & visual.red_mask;
					if (g_shift >= 0)
						pixel |= (g << g_shift) & visual.green_mask;
					else
						pixel |= (g >> (0-g_shift)) & visual.green_mask;
					if (b_shift >= 0)
						pixel |= (b << b_shift) & visual.blue_mask;
					else
						pixel |= (b >> (0-b_shift)) & visual.blue_mask;
				}
deuce's avatar
deuce committed
569
#ifdef XPutPixel
Deucе's avatar
Deucе committed
570
				XPutPixel(xim, x, y, pixel);
deuce's avatar
deuce committed
571
#else
Deucе's avatar
Deucе committed
572
				x11.XPutPixel(xim, x, y, pixel);
deuce's avatar
deuce committed
573
#endif
Deucе's avatar
Deucе committed
574
			}
575 576 577
			idx++;
		}
		/* This line was changed */
Deucе's avatar
Deucе committed
578
		// TODO: Previously this did one update per display line...
Deucе's avatar
Deucе committed
579
		if (last && cright >= 0 && (cbottom != y || y == source->h - 1)) {
580 581 582
			x11.XPutImage(dpy, win, gc, xim, cleft, ctop
			    , cleft + xoff, ctop + yoff
			    , (cright - cleft + 1), (cbottom - ctop + 1));
Deucе's avatar
Deucе committed
583
			cleft = source->w;
Deucе's avatar
Deucе committed
584
			cright = cbottom = -100;
Deucе's avatar
Deucе committed
585
			ctop = source->h;
deuce's avatar
deuce committed
586 587 588
		}
	}

589
	if (last == NULL)
Deucе's avatar
Deucе committed
590
		x11.XPutImage(dpy, win, gc, xim, 0, 0, xoff, yoff, source->w, source->h);
591
	else
Deucе's avatar
Deucе committed
592 593
		release_buffer(last);
	last = source;
deuce's avatar
deuce committed
594 595
}

596 597 598 599 600
static void handle_resize_event(int width, int height)
{
	int newFSH=1;
	int newFSW=1;

601 602 603
	aspect_fix(&width, &height, x_cvstat.aspect_width, x_cvstat.aspect_height);
	newFSH=width / bitmap_width;
	newFSW=height / bitmap_height;
604 605 606 607 608
	if(newFSW<1)
		newFSW=1;
	if(newFSH<1)
		newFSH=1;
	if(newFSH<newFSW)
609
		x_setscaling(newFSH);
610
	else
611 612 613 614
		x_setscaling(newFSW);
	old_scaling = x_cvstat.scaling;
	if(x_cvstat.scaling > 16)
		x_setscaling(16);
615

616
	/*
617 618
	 * We only need to resize if the width/height are not even multiples,
	 * or if the two axis don't scale the same way.
619 620
	 * Otherwise, we can simply resend everything
	 */
621 622 623 624
	if (newFSH != newFSW)
		resize_window();
	else
		resize_xim();
625
	bitmap_drv_request_pixels();
626 627
}

628
static void expose_rect(int x, int y, int width, int height)
629 630
{
	int sx,sy,ex,ey;
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
	int xoff=0, yoff=0;

	xoff = (x11_window_width - xim->width) / 2;
	if (xoff < 0)
		xoff = 0;
	yoff = (x11_window_height - xim->height) / 2;
	if (yoff < 0)
		yoff = 0;

	if (xoff > 0 || yoff > 0) {
		if (x < xoff || y < yoff || x + width > xoff + xim->width || y + height > yoff + xim->height) {
			x11.XFillRectangle(dpy, win, gc, 0, 0, x11_window_width, yoff);
			x11.XFillRectangle(dpy, win, gc, 0, yoff, xoff, yoff + xim->height);
			x11.XFillRectangle(dpy, win, gc, xoff+xim->width, yoff, x11_window_width, yoff + xim->height);
			x11.XFillRectangle(dpy, win, gc, 0, yoff + xim->height, x11_window_width, x11_window_height);
		}
	}
648

649
	sx=(x-xoff)/x_cvstat.scaling;
650
	sy=(y-yoff)/(x_cvstat.scaling);
651 652 653 654 655 656 657 658 659 660 661
	if (sx < 0)
		sx = 0;
	if (sy < 0)
		sy = 0;

	ex=(x-xoff)+width-1;
	ey=(y-yoff)+height-1;
	if (ex < 0)
		ex = 0;
	if (ey < 0)
		ey = 0;
662 663
	if((ex+1)%x_cvstat.scaling) {
		ex += x_cvstat.scaling-(ex%x_cvstat.scaling);
664
	}
665 666
	if((ey+1)%(x_cvstat.scaling)) {
		ey += x_cvstat.scaling-(ey%(x_cvstat.scaling));
667
	}
668
	ex=ex/x_cvstat.scaling;
669
	ey=ey/(x_cvstat.scaling);
670

671 672
	/* Since we're exposing, we *have* to redraw */
	if (last) {
Deucе's avatar
Deucе committed
673
		release_buffer(last);
674
		last = NULL;
675
		bitmap_drv_request_some_pixels(sx, sy, ex-sx+1, ey-sy+1);
676
	}
677 678 679
	// Do nothing...
	if (sx == ex || sy == ey)
		return;
680
	bitmap_drv_request_some_pixels(sx, sy, ex-sx+1, ey-sy+1);
681 682
}

683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
static bool
xlat_mouse_xy(int *x, int *y)
{
	int xoff, yoff;

	xoff = (x11_window_width - xim->width) / 2;
	if (xoff < 0)
		xoff = 0;
	yoff = (x11_window_height - xim->height) / 2;
	if (yoff < 0)
		yoff = 0;

	if (*x < xoff)
		return false;
	if (*y < yoff)
		return false;
	*x -= xoff;
	*y -= yoff;
	if (*x >= xim->width)
		return false;
	if (*y >= xim->height)
		return false;
	*x *= x_cvstat.scrnwidth;
	*y *= x_cvstat.scrnheight;
	*x /= xim->width;
	*y /= xim->height;
	return true;
}

deuce's avatar
deuce committed
712 713
static int x11_event(XEvent *ev)
{
deuce's avatar
deuce committed
714 715
	if (x11.XFilterEvent(ev, win))
		return 0;
deuce's avatar
deuce committed
716
	switch (ev->type) {
deuce's avatar
deuce committed
717 718 719 720 721 722
		case ClientMessage:
			if (ev->xclient.format == 32 && ev->xclient.data.l[0] == WM_DELETE_WINDOW) {
				uint16_t key=CIO_KEY_QUIT;
				write(key_pipe[1], &key, 2);
			}
			break;
deuce's avatar
deuce committed
723
		/* Graphics related events */
724 725 726
		case ConfigureNotify: {
			int width, height;

727
			if (x11_window_xpos != ev->xconfigure.x || x11_window_ypos != ev->xconfigure.y) {
Deucе's avatar
Deucе committed
728 729
				x11_window_xpos=ev->xconfigure.x;
				x11_window_ypos=ev->xconfigure.y;
730 731
			}
			if (x11_window_width != ev->xconfigure.width || x11_window_height != ev->xconfigure.height) {
Deucе's avatar
Deucе committed
732 733 734
				x11_window_width=ev->xconfigure.width;
				x11_window_height=ev->xconfigure.height;
				handle_resize_event(ev->xconfigure.width, ev->xconfigure.height);
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
				break;
			}
			width = bitmap_width * x_cvstat.scaling;
			height = bitmap_height * x_cvstat.scaling;

			aspect_correct(&width, &height, x_cvstat.aspect_width, x_cvstat.aspect_height);
			if (ev->xconfigure.width != width || ev->xconfigure.height != height) {
				// We can't have the size we requested... accept the size we got.
				int newFSH=1;
				int newFSW=1;

				width = ev->xconfigure.width;
				height = ev->xconfigure.height;
				aspect_fix(&width, &height, x_cvstat.aspect_width, x_cvstat.aspect_height);
				newFSH=width / bitmap_width;
				newFSW=height / bitmap_height;
				if(newFSW<1)
					newFSW=1;
				if(newFSH<1)
					newFSH=1;
				if(newFSH<newFSW)
					x_setscaling(newFSH);
				else
					x_setscaling(newFSW);
				old_scaling = x_cvstat.scaling;
				if(x_cvstat.scaling > 16)
					x_setscaling(16);

				resize_xim();
				bitmap_drv_request_pixels();
Deucе's avatar
Deucе committed
765
			}
deuce's avatar
deuce committed
766
			break;
767
		}
768 769 770
		case NoExpose:
			break;
		case GraphicsExpose:
771
			expose_rect(ev->xgraphicsexpose.x, ev->xgraphicsexpose.y, ev->xgraphicsexpose.width, ev->xgraphicsexpose.height);
deuce's avatar
deuce committed
772
			break;
773
		case Expose:
774
			expose_rect(ev->xexpose.x, ev->xexpose.y, ev->xexpose.width, ev->xexpose.height);
deuce's avatar
deuce committed
775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797
			break;

		/* Copy/Paste events */
		case SelectionClear:
			{
				XSelectionClearEvent *req;

				req=&(ev->xselectionclear);
				pthread_mutex_lock(&copybuf_mutex);
				if(req->selection==CONSOLE_CLIPBOARD)
					FREE_AND_NULL(copybuf);
				pthread_mutex_unlock(&copybuf_mutex);
			}
			break;
		case SelectionNotify:
			{
				int format;
				unsigned long len, bytes_left, dummy;

				if(ev->xselection.selection != CONSOLE_CLIPBOARD)
					break;
				if(ev->xselection.requestor!=win)
					break;
798
				if(ev->xselection.property) {
deuce's avatar
deuce committed
799
					x11.XGetWindowProperty(dpy, win, ev->xselection.property, 0, 0, True, AnyPropertyType, &pastebuf_format, &format, &len, &bytes_left, (unsigned char **)(&pastebuf));
800
					if(bytes_left > 0 && format==8) {
deuce's avatar
deuce committed
801
						x11.XGetWindowProperty(dpy, win, ev->xselection.property, 0, bytes_left, True, AnyPropertyType, &pastebuf_format, &format, &len, &dummy, (unsigned char **)&pastebuf);
802 803
						if (x11.utf8 && pastebuf_format == x11.utf8) {
							char *opb = pastebuf;
deuce's avatar
deuce committed
804
							pastebuf = (char *)utf8_to_cp(CIOLIB_ISO_8859_1, (uint8_t *)pastebuf, '?', strlen(pastebuf), NULL);
805 806 807 808 809 810
							if (pastebuf == NULL)
								pastebuf = opb;
							else
								x11.XFree(opb);
						}
					}
811 812 813
					else
						pastebuf=NULL;
				}
deuce's avatar
deuce committed
814 815 816 817 818 819
				else
					pastebuf=NULL;

				/* Set paste buffer */
				sem_post(&pastebuf_set);
				sem_wait(&pastebuf_used);
820 821 822 823
				if (x11.utf8 && pastebuf_format == x11.utf8)
					free(pastebuf);
				else
					x11.XFree(pastebuf);
deuce's avatar
deuce committed
824 825 826 827 828 829 830
				pastebuf=NULL;
			}
			break;
		case SelectionRequest:
			{
				XSelectionRequestEvent *req;
				XEvent respond;
deuce's avatar
deuce committed
831 832
				Atom supported[3];
				int count = 0;
deuce's avatar
deuce committed
833 834 835

				req=&(ev->xselectionrequest);
				pthread_mutex_lock(&copybuf_mutex);
deuce's avatar
deuce committed
836 837 838 839
				if (x11.targets == 0)
					x11.targets = x11.XInternAtom(dpy, "TARGETS", False);
				respond.xselection.property=None;
				if(copybuf!=NULL) {
deuce's avatar
deuce committed
840
					if(req->target==XA_STRING) {
deuce's avatar
deuce committed
841
						char *cpstr = utf8_to_cp(CIOLIB_ISO_8859_1, (uint8_t *)copybuf, '?', strlen(copybuf), NULL);
deuce's avatar
deuce committed
842 843
						if (cpstr != NULL) {
							x11.XChangeProperty(dpy, req->requestor, req->property, XA_STRING, 8, PropModeReplace, (uint8_t *)cpstr, strlen((char *)cpstr));
844
							respond.xselection.property=req->property;
deuce's avatar
deuce committed
845
							free(cpstr);
846 847
						}
					}
deuce's avatar
deuce committed
848 849 850 851
					else if(req->target == x11.utf8) {
						x11.XChangeProperty(dpy, req->requestor, req->property, x11.utf8, 8, PropModeReplace, (uint8_t *)copybuf, strlen((char *)copybuf));
						respond.xselection.property=req->property;
					}
852
					else if(req->target == x11.targets) {
deuce's avatar
deuce committed
853 854 855 856 857 858 859 860 861
						if (x11.utf8 == 0)
							x11.utf8 = x11.XInternAtom(dpy, "UTF8_STRING", False);

						supported[count++] = x11.targets;
						supported[count++] = XA_STRING;
						if (x11.utf8)
							supported[count++] = x11.utf8;
						x11.XChangeProperty(dpy, req->requestor, req->property, XA_ATOM, 32, PropModeReplace, (unsigned char *)supported, count);
						respond.xselection.property=req->property;
862
					}
deuce's avatar
deuce committed
863 864 865 866
				}
				respond.xselection.requestor=req->requestor;
				respond.xselection.selection=req->selection;
				respond.xselection.time=req->time;
deuce's avatar
deuce committed
867 868 869
				respond.xselection.target=req->target;
				respond.xselection.type=SelectionNotify;
				respond.xselection.display=req->display;
deuce's avatar
deuce committed
870
				x11.XSendEvent(dpy,req->requestor,0,0,&respond);
deuce's avatar
deuce committed
871
				x11.XFlush(dpy);
deuce's avatar
deuce committed
872 873 874 875 876 877 878 879
				pthread_mutex_unlock(&copybuf_mutex);
			}
			break;

		/* Mouse Events */
		case MotionNotify:
			{
				XMotionEvent *me = (XMotionEvent *)ev;
880 881
				if (!xlat_mouse_xy(&me->x, &me->y))
					break;
882 883 884 885 886
				int x_res = me->x;
				int y_res = me->y;

				me->x /= x_cvstat.charwidth;
				me->y /= x_cvstat.charheight;
deuce's avatar
deuce committed
887 888 889 890 891 892
				me->x++;
				me->y++;
				if(me->x<1)
					me->x=1;
				if(me->y<1)
					me->y=1;
893 894 895 896
				if(me->x>x_cvstat.cols)
					me->x=x_cvstat.cols;
				if(me->y>x_cvstat.rows+1)
					me->y=x_cvstat.rows+1;
897
				ciomouse_gotevent(CIOLIB_MOUSE_MOVE,me->x,me->y, x_res, y_res);
deuce's avatar
deuce committed
898 899 900 901 902
	    	}
			break;
		case ButtonRelease:
			{
				XButtonEvent *be = (XButtonEvent *)ev;
903 904
				if (!xlat_mouse_xy(&be->x, &be->y))
					break;
905 906
				int x_res = be->x;
				int y_res = be->y;
deuce's avatar
deuce committed
907

908 909
				be->x/=x_cvstat.charwidth;
				be->y/=x_cvstat.charheight;
deuce's avatar
deuce committed
910 911 912 913 914 915
				be->x++;
				be->y++;
				if(be->x<1)
					be->x=1;
				if(be->y<1)
					be->y=1;
916 917 918 919
				if(be->x>x_cvstat.cols)
					be->x=x_cvstat.cols;
				if(be->y>x_cvstat.rows+1)
					be->y=x_cvstat.rows+1;
deuce's avatar
deuce committed
920
				if (be->button <= 3) {
921
					ciomouse_gotevent(CIOLIB_BUTTON_RELEASE(be->button),be->x,be->y, x_res, y_res);
deuce's avatar
deuce committed
922 923 924 925 926 927
				}
	    	}
			break;
		case ButtonPress:
			{
				XButtonEvent *be = (XButtonEvent *)ev;
928 929
				if (!xlat_mouse_xy(&be->x, &be->y))
					break;
930 931
				int x_res = be->x;
				int y_res = be->y;
deuce's avatar
deuce committed
932

933 934
				be->x/=x_cvstat.charwidth;
				be->y/=x_cvstat.charheight;
deuce's avatar
deuce committed
935 936 937 938 939 940
				be->x++;
				be->y++;
				if(be->x<1)
					be->x=1;
				if(be->y<1)
					be->y=1;
941 942 943 944
				if(be->x>x_cvstat.cols)
					be->x=x_cvstat.cols;
				if(be->y>x_cvstat.rows+1)
					be->y=x_cvstat.rows+1;
945
				if (be->button <= 5) {
946
					ciomouse_gotevent(CIOLIB_BUTTON_PRESS(be->button),be->x,be->y, x_res, y_res);
deuce's avatar
deuce committed
947 948 949 950 951 952 953 954
				}
	    	}
			break;

		/* Keyboard Events */
		case KeyPress:
			{
				static char buf[128];
deuce's avatar
deuce committed
955
				static wchar_t wbuf[128];
deuce's avatar
deuce committed
956 957 958
				KeySym ks;
				int nlock = 0;
				WORD scan = 0xffff;
deuce's avatar
deuce committed
959 960 961 962
				Status lus = 0;
				int cnt;
				int i;
				uint8_t ch;
deuce's avatar
deuce committed
963

deuce's avatar
deuce committed
964 965 966 967 968 969
				if (ic)
					cnt = x11.XwcLookupString(ic, (XKeyPressedEvent *)ev, wbuf, sizeof(wbuf)/sizeof(wbuf[0]), &ks, &lus);
				else {
					cnt = x11.XLookupString((XKeyEvent *)ev, buf, sizeof(buf), &ks, NULL);
					lus = XLookupKeySym;
				}
deuce's avatar
deuce committed
970

deuce's avatar
deuce committed
971 972 973 974 975
				switch(lus) {
					case XLookupNone:
						ks = 0xffff;
						break;
					case XLookupBoth:
976 977 978
					case XLookupChars:
						if (lus == XLookupChars || ((ev->xkey.state & (Mod1Mask | ControlMask)) == 0)) {
							for (i = 0; i < cnt; i++) {
979 980 981 982
								if (wbuf[i] < 127)
									ch = wbuf[i];
								else
									ch = cpchar_from_unicode_cpoint(getcodepage(), wbuf[i], 0);
983 984 985
								if (ch) {
									write(key_pipe[1], &ch, 1);
								}
deuce's avatar
deuce committed
986
							}
987
							break;
deuce's avatar
deuce committed
988
						}
989
						// Fallthrough
deuce's avatar
deuce committed
990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033