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.7 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;
deuce's avatar
deuce committed
74
static XImage *xim;
deuce's avatar
deuce committed
75 76
static XIM im;
static XIC ic;
deuce's avatar
deuce committed
77 78 79 80
static unsigned int depth=0;
static int xfd;
static unsigned long black;
static unsigned long white;
81 82
static int bitmap_width=0;
static int bitmap_height=0;
83
static int old_scaling = 0;
84
struct video_stats x_cvstat;
85 86 87 88
static unsigned long base_pixel;
static int r_shift;
static int g_shift;
static int b_shift;
Deucе's avatar
Deucе committed
89
static struct graphics_buffer *last = NULL;
90

deuce's avatar
deuce committed
91
/* Array of Graphics Contexts */
92
static GC gc;
deuce's avatar
deuce committed
93 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

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 */
201 202
    {	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
203 204 205 206 207 208 209
    {	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
210 211
static void resize_xim(void)
{
Deucе's avatar
Deucе committed
212
	int width = bitmap_width * x_cvstat.scaling;
213
	int height = bitmap_height * x_cvstat.scaling;
Deucе's avatar
Deucе committed
214

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

241 242 243 244 245 246 247 248 249 250 251 252 253
/* 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
254 255 256
/* Get a connection to the X server and create the window. */
static int init_window()
{
257 258
	XGCValues gcv;
	int i;
259
	XWMHints *wmhints;
deuce's avatar
deuce committed
260
	XClassHint *classhints;
261 262 263 264 265 266
	int ret;
	int best=-1;
	int best_depth=0;
	int best_cmap=0;
	XVisualInfo template = {0};
	XVisualInfo *vi;
deuce's avatar
deuce committed
267 268

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

276 277 278 279 280 281 282 283 284 285 286
	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;
287 288 289 290
		base_pixel = ULONG_MAX;
		base_pixel &= ~visual.red_mask;
		base_pixel &= ~visual.green_mask;
		base_pixel &= ~visual.blue_mask;
291 292 293
		r_shift = my_fls(visual.red_mask)-16;
		g_shift = my_fls(visual.green_mask)-16;
		b_shift = my_fls(visual.blue_mask)-16;
294 295 296 297 298 299 300 301
	}
	else {
		fprintf(stderr, "Unable to find TrueColor visual\n");
		x11.XFree(vi);
		return -1;
	}
	x11.XFree(vi);

deuce's avatar
deuce committed
302 303 304 305 306
	/* Allocate black and white */
	black=BlackPixel(dpy, DefaultScreen(dpy));
	white=WhitePixel(dpy, DefaultScreen(dpy));

    /* Create window, but defer setting a size and GC. */
307 308 309 310 311 312
	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,
313
			      640*x_cvstat.scaling, 400*x_cvstat.scaling, 2, depth, InputOutput, &visual, CWColormap | CWBorderPixel | CWBackPixel, &wa);
deuce's avatar
deuce committed
314

deuce's avatar
deuce committed
315 316 317
	classhints=x11.XAllocClassHint();
	if (classhints)
		classhints->res_name = classhints->res_class = "CIOLIB";
318 319 320
	wmhints=x11.XAllocWMHints();
	if(wmhints) {
		wmhints->initial_state=NormalState;
deuce's avatar
deuce committed
321
		wmhints->flags = (StateHint/* | IconPixmapHint | IconMaskHint*/ | InputHint);
322
		wmhints->input = True;
deuce's avatar
deuce committed
323 324
		x11.XSetWMProperties(dpy, win, NULL, NULL, 0, 0, NULL, wmhints, classhints);
		x11.XFree(wmhints);
325
	}
deuce's avatar
deuce committed
326 327 328 329 330 331 332
	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
333 334
	if (classhints)
		x11.XFree(classhints);
335

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

deuce's avatar
deuce committed
338
	gcv.function = GXcopy;
339 340
	gcv.foreground = black | 0xff000000;
	gcv.background = white;
deuce's avatar
deuce committed
341
	gcv.graphics_exposures = False;
342
	gc=x11.XCreateGC(dpy, win, GCFunction | GCForeground | GCBackground | GCGraphicsExposures, &gcv);
deuce's avatar
deuce committed
343

344
	x11.XSelectInput(dpy, win, KeyReleaseMask | KeyPressMask |
deuce's avatar
deuce committed
345 346 347
		     ExposureMask | ButtonPressMask
		     | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask);

348
	x11.XStoreName(dpy, win, "SyncConsole");
deuce's avatar
deuce committed
349
	x11.XSetWMProtocols(dpy, win, &WM_DELETE_WINDOW, 1);
deuce's avatar
deuce committed
350 351 352 353

	return(0);
}

354 355 356 357
/*
 * Actually maps (shows) the window
 */
static void map_window()
deuce's avatar
deuce committed
358
{
359 360 361
	XSizeHints *sh;
	int minwidth = bitmap_width;
	int minheight = bitmap_height;
deuce's avatar
deuce committed
362

363 364
	sh = x11.XAllocSizeHints();
	if (sh == NULL) {
deuce's avatar
deuce committed
365 366 367 368
		fprintf(stderr, "Could not get XSizeHints structure");
		exit(1);
	}

369 370
	sh->base_width = bitmap_width * x_cvstat.scaling;
	sh->base_height = bitmap_height * x_cvstat.scaling;
deuce's avatar
deuce committed
371

372 373
	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
374

375 376
	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
377

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

380 381
	x11.XSetWMNormalHints(dpy, win, sh);
	x11.XMapWindow(dpy, win);
deuce's avatar
deuce committed
382

383 384 385
	x11.XFree(sh);

	return;
deuce's avatar
deuce committed
386 387
}

388 389 390
/* Resize the window. This function is called after a mode change. */
static void resize_window()
{
391 392 393 394 395
	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
396
	resize_xim();
deuce's avatar
deuce committed
397

Deucе's avatar
Deucе committed
398
	return;
399 400
}

401
static void init_mode_internal(int mode)
deuce's avatar
deuce committed
402
{
403
	int oldcols;
deuce's avatar
deuce committed
404

405
	oldcols=x_cvstat.cols;
406

407
	pthread_mutex_lock(&blinker_lock);
408
	pthread_mutex_lock(&vstatlock);
409
	if (last) {
Deucе's avatar
Deucе committed
410
		release_buffer(last);
411 412
		last = NULL;
	}
413
	bitmap_drv_init_mode(mode, &bitmap_width, &bitmap_height);
deuce's avatar
deuce committed
414

deuce's avatar
deuce committed
415
	/* Deal with 40 col doubling */
deuce's avatar
deuce committed
416
	if(oldcols != vstat.cols) {
deuce's avatar
deuce committed
417
		if(oldcols == 40)
deuce's avatar
deuce committed
418 419 420
			vstat.scaling /= 2;
		if(vstat.cols == 40)
			vstat.scaling *= 2;
deuce's avatar
deuce committed
421
	}
deuce's avatar
deuce committed
422 423
	if(vstat.scaling < 1)
		vstat.scaling = 1;
deuce's avatar
deuce committed
424

deuce's avatar
deuce committed
425 426
	x_cvstat = vstat;
	pthread_mutex_unlock(&vstatlock);
deuce's avatar
deuce committed
427
	pthread_mutex_unlock(&blinker_lock);
428
	map_window();
429 430 431 432 433
}

static void check_scaling(void)
{
	if (old_scaling != x_cvstat.scaling) {
434
		resize_window();
435 436 437 438 439 440 441
		old_scaling = x_cvstat.scaling;
	}
}

static int init_mode(int mode)
{
	init_mode_internal(mode);
442
	resize_window();
443
	bitmap_drv_request_pixels();
deuce's avatar
deuce committed
444 445

	sem_post(&mode_set);
446
	return(0);
deuce's avatar
deuce committed
447 448 449 450 451 452 453
}

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. */
454 455
	if(x_cvstat.scaling<1)
		x_setscaling(1);
deuce's avatar
deuce committed
456 457 458
    if(init_window())
		return(-1);

459
	bitmap_drv_init(x11_drawrect, x11_flush);
deuce's avatar
deuce committed
460 461

    /* Initialize mode 3 (text, 80x25, 16 colors) */
462
    init_mode_internal(3);
deuce's avatar
deuce committed
463 464 465 466

    return(0);
}

Deucе's avatar
Deucе committed
467 468
static void
local_draw_rect(struct rectlist *rect)
deuce's avatar
deuce committed
469
{
470
	int x, y, xoff = 0, yoff = 0;
471 472
	unsigned int r, g, b;
	unsigned long pixel;
Deucе's avatar
Deucе committed
473 474 475
	int cleft;
	int cright = -100;
	int ctop;
476
	int cbottom = -1;
477
	int idx;
Deucе's avatar
Deucе committed
478
	uint32_t last_pixel = 0x55555555;
Deucе's avatar
Deucе committed
479
	struct graphics_buffer *source;
Deucе's avatar
Deucе committed
480
	bool isRGB8 = false;
deuce's avatar
deuce committed
481

Deucе's avatar
Deucе committed
482 483
	if (bitmap_width != rect->rect.width || bitmap_height != rect->rect.height) {
		bitmap_drv_free_rect(rect);
484
		return;
Deucе's avatar
Deucе committed
485
	}
486 487 488 489 490 491 492 493

	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
494
	// Scale...
495
	source = do_scale(rect, x_cvstat.scaling, x_cvstat.scaling, x_cvstat.aspect_width, x_cvstat.aspect_height);
Deucе's avatar
Deucе committed
496 497
	bitmap_drv_free_rect(rect);
	if (source == NULL)
Deucе's avatar
Deucе committed
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
		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
513

514
	/* TODO: Translate into local colour depth */
Deucе's avatar
Deucе committed
515
	idx = 0;
Deucе's avatar
Deucе committed
516 517 518
	if (visual.red_mask == 0xff0000 && visual.green_mask == 0x00ff00 && visual.blue_mask == 0x0000ff)
		isRGB8 = true;

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

584
	if (last == NULL)
Deucе's avatar
Deucе committed
585
		x11.XPutImage(dpy, win, gc, xim, 0, 0, xoff, yoff, source->w, source->h);
586
	else
Deucе's avatar
Deucе committed
587 588
		release_buffer(last);
	last = source;
deuce's avatar
deuce committed
589 590
}

591 592 593 594 595
static void handle_resize_event(int width, int height)
{
	int newFSH=1;
	int newFSW=1;

596 597 598
	aspect_fix(&width, &height, x_cvstat.aspect_width, x_cvstat.aspect_height);
	newFSH=width / bitmap_width;
	newFSW=height / bitmap_height;
599 600 601 602 603
	if(newFSW<1)
		newFSW=1;
	if(newFSH<1)
		newFSH=1;
	if(newFSH<newFSW)
604
		x_setscaling(newFSH);
605
	else
606 607 608 609
		x_setscaling(newFSW);
	old_scaling = x_cvstat.scaling;
	if(x_cvstat.scaling > 16)
		x_setscaling(16);
610

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

623
static void expose_rect(int x, int y, int width, int height)
624 625
{
	int sx,sy,ex,ey;
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
	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);
		}
	}
643

644
	sx=(x-xoff)/x_cvstat.scaling;
645
	sy=(y-yoff)/(x_cvstat.scaling);
646 647 648 649 650 651 652 653 654 655 656
	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;
657 658
	if((ex+1)%x_cvstat.scaling) {
		ex += x_cvstat.scaling-(ex%x_cvstat.scaling);
659
	}
660 661
	if((ey+1)%(x_cvstat.scaling)) {
		ey += x_cvstat.scaling-(ey%(x_cvstat.scaling));
662
	}
663
	ex=ex/x_cvstat.scaling;
664
	ey=ey/(x_cvstat.scaling);
665

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

678 679 680 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
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
707 708
static int x11_event(XEvent *ev)
{
deuce's avatar
deuce committed
709 710
	if (x11.XFilterEvent(ev, win))
		return 0;
deuce's avatar
deuce committed
711
	switch (ev->type) {
deuce's avatar
deuce committed
712 713 714 715 716 717
		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
718
		/* Graphics related events */
719 720 721
		case ConfigureNotify: {
			int width, height;

722
			if (x11_window_xpos != ev->xconfigure.x || x11_window_ypos != ev->xconfigure.y) {
Deucе's avatar
Deucе committed
723 724
				x11_window_xpos=ev->xconfigure.x;
				x11_window_ypos=ev->xconfigure.y;
725 726
			}
			if (x11_window_width != ev->xconfigure.width || x11_window_height != ev->xconfigure.height) {
Deucе's avatar
Deucе committed
727 728 729
				x11_window_width=ev->xconfigure.width;
				x11_window_height=ev->xconfigure.height;
				handle_resize_event(ev->xconfigure.width, ev->xconfigure.height);
730 731 732 733 734 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
				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
760
			}
deuce's avatar
deuce committed
761
			break;
762
		}
763 764 765
		case NoExpose:
			break;
		case GraphicsExpose:
766
			expose_rect(ev->xgraphicsexpose.x, ev->xgraphicsexpose.y, ev->xgraphicsexpose.width, ev->xgraphicsexpose.height);
deuce's avatar
deuce committed
767
			break;
768
		case Expose:
769
			expose_rect(ev->xexpose.x, ev->xexpose.y, ev->xexpose.width, ev->xexpose.height);
deuce's avatar
deuce committed
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792
			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;
793
				if(ev->xselection.property) {
deuce's avatar
deuce committed
794
					x11.XGetWindowProperty(dpy, win, ev->xselection.property, 0, 0, True, AnyPropertyType, &pastebuf_format, &format, &len, &bytes_left, (unsigned char **)(&pastebuf));
795
					if(bytes_left > 0 && format==8) {
deuce's avatar
deuce committed
796
						x11.XGetWindowProperty(dpy, win, ev->xselection.property, 0, bytes_left, True, AnyPropertyType, &pastebuf_format, &format, &len, &dummy, (unsigned char **)&pastebuf);
797 798
						if (x11.utf8 && pastebuf_format == x11.utf8) {
							char *opb = pastebuf;
deuce's avatar
deuce committed
799
							pastebuf = (char *)utf8_to_cp(CIOLIB_ISO_8859_1, (uint8_t *)pastebuf, '?', strlen(pastebuf), NULL);
800 801 802 803 804 805
							if (pastebuf == NULL)
								pastebuf = opb;
							else
								x11.XFree(opb);
						}
					}
806 807 808
					else
						pastebuf=NULL;
				}
deuce's avatar
deuce committed
809 810 811 812 813 814
				else
					pastebuf=NULL;

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

				req=&(ev->xselectionrequest);
				pthread_mutex_lock(&copybuf_mutex);
deuce's avatar
deuce committed
831 832 833 834
				if (x11.targets == 0)
					x11.targets = x11.XInternAtom(dpy, "TARGETS", False);
				respond.xselection.property=None;
				if(copybuf!=NULL) {
deuce's avatar
deuce committed
835
					if(req->target==XA_STRING) {
deuce's avatar
deuce committed
836
						char *cpstr = utf8_to_cp(CIOLIB_ISO_8859_1, (uint8_t *)copybuf, '?', strlen(copybuf), NULL);
deuce's avatar
deuce committed
837 838
						if (cpstr != NULL) {
							x11.XChangeProperty(dpy, req->requestor, req->property, XA_STRING, 8, PropModeReplace, (uint8_t *)cpstr, strlen((char *)cpstr));
839
							respond.xselection.property=req->property;
deuce's avatar
deuce committed
840
							free(cpstr);
841 842
						}
					}
deuce's avatar
deuce committed
843 844 845 846
					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;
					}
847
					else if(req->target == x11.targets) {
deuce's avatar
deuce committed
848 849 850 851 852 853 854 855 856
						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;
857
					}
deuce's avatar
deuce committed
858 859 860 861
				}
				respond.xselection.requestor=req->requestor;
				respond.xselection.selection=req->selection;
				respond.xselection.time=req->time;
deuce's avatar
deuce committed
862 863 864
				respond.xselection.target=req->target;
				respond.xselection.type=SelectionNotify;
				respond.xselection.display=req->display;
deuce's avatar
deuce committed
865
				x11.XSendEvent(dpy,req->requestor,0,0,&respond);
deuce's avatar
deuce committed
866
				x11.XFlush(dpy);
deuce's avatar
deuce committed
867 868 869 870 871 872 873 874
				pthread_mutex_unlock(&copybuf_mutex);
			}
			break;

		/* Mouse Events */
		case MotionNotify:
			{
				XMotionEvent *me = (XMotionEvent *)ev;
875 876
				if (!xlat_mouse_xy(&me->x, &me->y))
					break;
877 878 879 880 881
				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
882 883 884 885 886 887
				me->x++;
				me->y++;
				if(me->x<1)
					me->x=1;
				if(me->y<1)
					me->y=1;
888 889 890 891
				if(me->x>x_cvstat.cols)
					me->x=x_cvstat.cols;
				if(me->y>x_cvstat.rows+1)
					me->y=x_cvstat.rows+1;
892
				ciomouse_gotevent(CIOLIB_MOUSE_MOVE,me->x,me->y, x_res, y_res);
deuce's avatar
deuce committed
893 894 895 896 897
	    	}
			break;
		case ButtonRelease:
			{
				XButtonEvent *be = (XButtonEvent *)ev;
898 899
				if (!xlat_mouse_xy(&be->x, &be->y))
					break;
900 901
				int x_res = be->x;
				int y_res = be->y;
deuce's avatar
deuce committed
902

903 904
				be->x/=x_cvstat.charwidth;
				be->y/=x_cvstat.charheight;
deuce's avatar
deuce committed
905 906 907 908 909 910
				be->x++;
				be->y++;
				if(be->x<1)
					be->x=1;
				if(be->y<1)
					be->y=1;
911 912 913 914
				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
915
				if (be->button <= 3) {
916
					ciomouse_gotevent(CIOLIB_BUTTON_RELEASE(be->button),be->x,be->y, x_res, y_res);
deuce's avatar
deuce committed
917 918 919 920 921 922
				}
	    	}
			break;
		case ButtonPress:
			{
				XButtonEvent *be = (XButtonEvent *)ev;
923 924
				if (!xlat_mouse_xy(&be->x, &be->y))
					break;
925 926
				int x_res = be->x;
				int y_res = be->y;
deuce's avatar
deuce committed
927

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

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

deuce's avatar
deuce committed
959 960 961 962 963 964
				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
965

deuce's avatar
deuce committed
966 967 968 969 970
				switch(lus) {
					case XLookupNone:
						ks = 0xffff;
						break;
					case XLookupBoth:
971 972 973
					case XLookupChars:
						if (lus == XLookupChars || ((ev->xkey.state & (Mod1Mask | ControlMask)) == 0)) {
							for (i = 0; i < cnt; i++) {
974
								ch = cpchar_from_unicode_cpoint(getcodepage(), wbuf[i], 0);
975 976 977
								if (ch) {
									write(key_pipe[1], &ch, 1);
								}
deuce's avatar
deuce committed
978
							}
979
							break;
deuce's avatar
deuce committed
980
						}
981
						// Fallthrough
deuce's avatar
deuce committed
982 983 984 985 986 987 988 989 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