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

bitmap_con.c 42.6 KB
Newer Older
1
/* $Id: bitmap_con.c,v 1.148 2020/06/27 00:04:44 deuce Exp $ */
deuce's avatar
deuce committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

#include <stdarg.h>
#include <stdio.h>		/* NULL */
#include <stdlib.h>
#include <string.h>

#include "threadwrap.h"
#include "semwrap.h"
#include "gen_defs.h"
#include "genwrap.h"
#include "dirwrap.h"
#include "xpbeep.h"

#if (defined CIOLIB_IMPORTS)
 #undef CIOLIB_IMPORTS
#endif
#if (defined CIOLIB_EXPORTS)
 #undef CIOLIB_EXPORTS
#endif

#include "ciolib.h"
#include "vidmodes.h"
deuce's avatar
deuce committed
24
#include "bitmap_con.h"
deuce's avatar
deuce committed
25

26
static uint32_t palette[65536];
27

28 29
#if 0

30
static int dbg_pthread_mutex_lock(pthread_mutex_t *lptr, unsigned line)
31 32 33 34 35 36 37 38
{
	int ret = pthread_mutex_lock(lptr);

	if (ret)
		fprintf(stderr, "pthread_mutex_lock() returned %d at %u\n", ret, line);
	return ret;
}

39
static int dbg_pthread_mutex_unlock(pthread_mutex_t *lptr, unsigned line)
40 41 42 43 44 45 46 47
{
	int ret = pthread_mutex_unlock(lptr);

	if (ret)
		fprintf(stderr, "pthread_mutex_lock() returned %d at %u\n", ret, line);
	return ret;
}

48
static int dbg_pthread_mutex_trylock(pthread_mutex_t *lptr, unsigned line)
49 50 51 52 53 54 55 56 57 58 59 60 61 62
{
	int ret = pthread_mutex_trylock(lptr);

	if (ret)
		fprintf(stderr, "pthread_mutex_lock() returned %d at %u\n", ret, line);
	return ret;
}

#define pthread_mutex_lock(a)		dbg_pthread_mutex_lock(a, __LINE__)
#define pthread_mutex_unlock(a)		dbg_pthread_mutex_unlock(a, __LINE__)
#define pthread_mutex_trylock(a)	dbg_pthread_trymutex_lock(a, __LINE__)

#endif

63 64 65
/* Structs */

struct bitmap_screen {
66 67 68
	uint32_t *screen;
	int		screenwidth;
	int		screenheight;
69
	pthread_mutex_t	screenlock;
70
	int		update_pixels;
71
};
deuce's avatar
deuce committed
72 73

struct bitmap_callbacks {
74
	void	(*drawrect)		(struct rectlist *data);
75
	void	(*flush)		(void);
deuce's avatar
deuce committed
76
	pthread_mutex_t lock;
77
	unsigned rects;
deuce's avatar
deuce committed
78 79
};

80
/* Static globals */
81

82 83 84
static int default_font=-99;
static int current_font[4]={-99, -99, -99, -99};
static int bitmap_initialized=0;
85 86
static struct bitmap_screen screena;
static struct bitmap_screen screenb;
87 88 89 90
struct video_stats vstat;
static struct bitmap_callbacks callbacks;
static unsigned char *font[4];
static int force_redraws=0;
Deucе's avatar
Deucе committed
91
static int force_cursor=0;
92
struct rectlist *free_rects;
93
pthread_mutex_t free_rect_lock;
deuce's avatar
deuce committed
94

95
/* The read lock must be held here. */
96
#define PIXEL_OFFSET(screen, x, y)	( (y)*(screen).screenwidth+(x) )
deuce's avatar
deuce committed
97

98
/* Exported globals */
99

100
pthread_mutex_t		vstatlock;
101
pthread_mutex_t blinker_lock;
deuce's avatar
deuce committed
102

103
/* Forward declarations */
104

105
static int bitmap_loadfont_locked(char *filename);
106
static void set_vmem_cell(struct vstat_vmem *vmem_ptr, size_t pos, uint16_t cell, uint32_t fg, uint32_t bg);
107
static int bitmap_attr2palette_locked(uint8_t attr, uint32_t *fgp, uint32_t *bgp);
108
static void	cb_drawrect(struct rectlist *data);
109 110 111
static void request_redraw_locked(void);
static void request_redraw(void);
static void memset_u32(void *buf, uint32_t u, size_t len);
deuce's avatar
deuce committed
112
static int bitmap_draw_one_char(unsigned int xpos, unsigned int ypos);
113 114 115
static void cb_flush(void);
static int check_redraw(void);
static void blinker_thread(void *data);
116
static __inline struct bitmap_screen *current_screen(void);
deuce's avatar
deuce committed
117
static int update_from_vmem(int force);
118
static int bitmap_vmem_puttext_locked(int sx, int sy, int ex, int ey, struct vmem_cell *fill);
119
static uint32_t color_value(uint32_t col);
120

121 122 123
/**************************************************************/
/* These functions get called from the driver and ciolib only */
/**************************************************************/
124

125
static int bitmap_loadfont_locked(char *filename)
126
{
127 128 129 130 131 132
	static char current_filename[MAX_PATH];
	unsigned int fontsize;
	int fw;
	int fh;
	int i;
	FILE	*fontfile=NULL;
133

134
	if(!bitmap_initialized)
135
		return(0);
136 137 138 139 140 141 142 143 144 145 146 147 148
	if(current_font[0]==-99 || current_font[0]>(sizeof(conio_fontdata)/sizeof(struct conio_font_data_struct)-2)) {
		for(i=0; conio_fontdata[i].desc != NULL; i++) {
			if(!strcmp(conio_fontdata[i].desc, "Codepage 437 English")) {
				current_font[0]=i;
				break;
			}
		}
		if(conio_fontdata[i].desc==NULL)
			current_font[0]=0;
	}
	if(current_font[0]==-1)
		filename=current_filename;
	else if(conio_fontdata[current_font[0]].desc==NULL)
149
		return(0);
150

151 152 153 154 155 156 157 158
	for (i=1; i<sizeof(current_font)/sizeof(current_font[0]); i++) {
		if(current_font[i] == -1)
			;
		else if (current_font[i] < 0)
			current_font[i]=current_font[0];
		else if(conio_fontdata[current_font[i]].desc==NULL)
			current_font[i]=current_font[0];
	}
159

160 161
	fh=vstat.charheight;
	fw=vstat.charwidth/8+(vstat.charwidth%8?1:0);
162

163 164 165 166 167 168 169
	fontsize=fw*fh*256*sizeof(unsigned char);

	for (i=0; i<sizeof(font)/sizeof(font[0]); i++) {
		if(font[i])
			FREE_AND_NULL(font[i]);
		if((font[i]=(unsigned char *)malloc(fontsize))==NULL)
			goto error_return;
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 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231

	if(filename != NULL) {
		if(flength(filename)!=fontsize)
			goto error_return;
		if((fontfile=fopen(filename,"rb"))==NULL)
			goto error_return;
		if(fread(font[0], 1, fontsize, fontfile)!=fontsize)
			goto error_return;
		fclose(fontfile);
		fontfile=NULL;
		current_font[0]=-1;
		if(filename != current_filename)
			SAFECOPY(current_filename,filename);
		for (i=1; i<sizeof(font)/sizeof(font[0]); i++) {
			if (current_font[i]==-1)
				memcpy(font[i], font[0], fontsize);
		}
	}
	for (i=0; i<sizeof(font)/sizeof(font[0]); i++) {
		if (current_font[i] < 0)
			continue;
		switch(vstat.charwidth) {
			case 8:
				switch(vstat.charheight) {
					case 8:
						if(conio_fontdata[current_font[i]].eight_by_eight==NULL) {
							if (i==0)
								goto error_return;
							else
								FREE_AND_NULL(font[i]);
						}
						else
							memcpy(font[i], conio_fontdata[current_font[i]].eight_by_eight, fontsize);
						break;
					case 14:
						if(conio_fontdata[current_font[i]].eight_by_fourteen==NULL) {
							if (i==0)
								goto error_return;
							else
								FREE_AND_NULL(font[i]);
						}
						else
							memcpy(font[i], conio_fontdata[current_font[i]].eight_by_fourteen, fontsize);
						break;
					case 16:
						if(conio_fontdata[current_font[i]].eight_by_sixteen==NULL) {
							if (i==0)
								goto error_return;
							else
								FREE_AND_NULL(font[i]);
						}
						else
							memcpy(font[i], conio_fontdata[current_font[i]].eight_by_sixteen, fontsize);
						break;
					default:
						goto error_return;
				}
				break;
			default:
				goto error_return;
		}
232 233
	}

234
    return(1);
235 236 237 238 239 240

error_return:
	for (i=0; i<sizeof(font)/sizeof(font[0]); i++)
		FREE_AND_NULL(font[i]);
	if(fontfile)
		fclose(fontfile);
241
	return(0);
242 243 244 245 246 247 248 249 250 251
}

/***************************************************/
/* These functions get called from the driver only */
/***************************************************/

/***********************************************/
/* These functions get called from ciolib only */
/***********************************************/

252
static int bitmap_vmem_puttext_locked(int sx, int sy, int ex, int ey, struct vmem_cell *fill)
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
{
	int x,y;
	struct vstat_vmem *vmem_ptr;

	if(!bitmap_initialized)
		return(0);

	if(		   sx < 1
			|| sy < 1
			|| ex < 1
			|| ey < 1
			|| sx > cio_textinfo.screenwidth
			|| sy > cio_textinfo.screenheight
			|| sx > ex
			|| sy > ey
			|| ex > cio_textinfo.screenwidth
			|| ey > cio_textinfo.screenheight
			|| fill==NULL)
		return(0);

	pthread_mutex_lock(&vstatlock);
	vmem_ptr = get_vmem(&vstat);
	for(y=sy-1;y<ey;y++) {
		for(x=sx-1;x<ex;x++) {
277
			memcpy(&vmem_ptr->vmem[y*cio_textinfo.screenwidth+x], fill++, sizeof(*fill));
278
			bitmap_draw_one_char(x+1, y+1);
279 280 281 282 283 284 285
		}
	}
	release_vmem(vmem_ptr);
	pthread_mutex_unlock(&vstatlock);
	return(1);
}

286
static void set_vmem_cell(struct vstat_vmem *vmem_ptr, size_t pos, uint16_t cell, uint32_t fg, uint32_t bg)
287
{
288 289
	int		altfont;
	int		font;
290

291
	bitmap_attr2palette_locked(cell>>8, fg == 0xffffff ? &fg : NULL, bg == 0xffffff ? &bg : NULL);
292

293 294 295 296 297 298
	altfont = (cell>>11 & 0x01) | ((cell>>14) & 0x02);
	if (!vstat.bright_altcharset)
		altfont &= ~0x01;
	if (!vstat.blink_altcharset)
		altfont &= ~0x02;
	font=current_font[altfont];
299 300
	if (font == -99)
		font = default_font;
301 302 303 304 305 306 307 308
	if (font < 0 || font > 255)
		font = 0;

	vmem_ptr->vmem[pos].legacy_attr = cell >> 8;
	vmem_ptr->vmem[pos].ch = cell & 0xff;
	vmem_ptr->vmem[pos].fg = fg;
	vmem_ptr->vmem[pos].bg = bg;
	vmem_ptr->vmem[pos].font = font;
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
}

static int bitmap_attr2palette_locked(uint8_t attr, uint32_t *fgp, uint32_t *bgp)
{
	uint32_t fg = attr & 0x0f;
	uint32_t bg = (attr >> 4) & 0x0f;

	if(!vstat.bright_background)
		bg &= 0x07;
	if(vstat.no_bright)
		fg &= 0x07;

	if (fgp)
		*fgp = vstat.palette[fg];
	if (bgp)
		*bgp = vstat.palette[bg];

326
	return 1;
327 328 329 330 331 332
}

/**********************************************************************/
/* These functions get called from ciolib and the blinker thread only */
/**********************************************************************/

333 334
static int
cursor_visible_locked(void)
335
{
336 337 338 339 340 341 342 343
	if (!vstat.curs_visible)
		return 0;
	if (vstat.curs_start > vstat.curs_end)
		return 0;
	if (vstat.curs_blinks) {
		if (vstat.curs_blink)
			return 1;
		return 0;
344
	}
345
	return 1;
346 347
}

348
static void	cb_drawrect(struct rectlist *data)
349
{
350 351 352 353
	int x, y;
	uint32_t *pixel;
	uint32_t cv;

354 355
	if (data == NULL)
		return;
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
	/*
	 * Draw the cursor if it's visible
	 * 1) It's located at vstat.curs_col/vstat.curs_row.
	 * 2) The size is defined by vstat.curs_start and vstat.curs_end...
	 *    the are both rows from the top of the cell.
	 *    If vstat.curs_start > vstat.curs_end, the cursor is not shown.
	 * 3) If vstat.curs_visible is false, the cursor is not shown.
	 * 4) If vstat.curs_blinks is false, the cursor does not blink.
	 * 5) When blinking, the cursor is shown when vstat.blink is true.
	 */
	pthread_mutex_lock(&vstatlock);
	if (cursor_visible_locked()) {
		cv = color_value(ciolib_fg);
		for (y = vstat.curs_start; y <= vstat.curs_end; y++) {
			pixel = &data->data[((vstat.curs_row - 1) * vstat.charheight + y) * data->rect.width + (vstat.curs_col - 1) * vstat.charwidth];
			for (x = 0; x < vstat.charwidth; x++) {
				*(pixel++) = cv;
			}
		}
	}
	pthread_mutex_unlock(&vstatlock);
377
	pthread_mutex_lock(&callbacks.lock);
378
	callbacks.drawrect(data);
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
	callbacks.rects++;
	pthread_mutex_unlock(&callbacks.lock);
}

static void request_redraw_locked(void)
{
	force_redraws = 1;
}

static void request_redraw(void)
{
	pthread_mutex_lock(&vstatlock);
	request_redraw_locked();
	pthread_mutex_unlock(&vstatlock);
}
394

395 396 397
/*
 * Called with the screen lock held
 */
398
static struct rectlist *alloc_full_rect(struct bitmap_screen *screen)
399 400 401
{
	struct rectlist * ret;

402
	pthread_mutex_lock(&free_rect_lock);
403
	while (free_rects) {
404
		if (free_rects->rect.width == screen->screenwidth && free_rects->rect.height == screen->screenheight) {
405 406
			ret = free_rects;
			free_rects = free_rects->next;
407 408
			ret->next = NULL;
			ret->rect.x = ret->rect.y = 0;
409
			pthread_mutex_unlock(&free_rect_lock);
410 411 412 413 414 415 416 417 418
			return ret;
		}
		else {
			free(free_rects->data);
			ret = free_rects->next;
			free(free_rects);
			free_rects = ret;
		}
	}
419
	pthread_mutex_unlock(&free_rect_lock);
420 421

	ret = malloc(sizeof(struct rectlist));
422
	ret->next = NULL;
423 424
	ret->rect.x = 0;
	ret->rect.y = 0;
425 426
	ret->rect.width = screen->screenwidth;
	ret->rect.height = screen->screenheight;
427 428 429 430 431 432
	ret->data = malloc(ret->rect.width * ret->rect.height * sizeof(ret->data[0]));
	if (ret->data == NULL)
		FREE_AND_NULL(ret);
	return ret;
}

433 434 435 436
static uint32_t color_value(uint32_t col)
{
	if (col & 0x80000000)
		return col;
Deucе's avatar
Deucе committed
437 438
	if ((col & 0xffffff) < sizeof(palette) / sizeof(palette[0]))
		return (col & 0xff000000) | palette[col & 0xffffff];
Deucе's avatar
Deucе committed
439
	fprintf(stderr, "Invalid colour value: %08x\n", col);
440
	return 0xff000000;
441 442
}

443
static struct rectlist *get_full_rectangle_locked(struct bitmap_screen *screen)
444
{
445
	size_t i;
deuce's avatar
deuce committed
446
	struct rectlist *rect;
447

Deucе's avatar
Deucе committed
448
	// TODO: Some sort of caching here would make things faster...?
449
	if(callbacks.drawrect) {
450
		rect = alloc_full_rect(screen);
451 452
		if (!rect)
			return rect;
453 454
		for (i=0; i<screen->screenwidth*screen->screenheight; i++)
			rect->data[i] = color_value(screen->screen[i]);
455 456 457
		return rect;
	}
	return NULL;
458 459
}

460 461 462 463
static void memset_u32(void *buf, uint32_t u, size_t len)
{
	size_t i;
	char *cbuf = buf;
464

465 466 467
	for (i = 0; i < len; i++) {
		memcpy(cbuf, &u, sizeof(uint32_t));
		cbuf += sizeof(uint32_t);
468 469 470
	}
}

471
/*
deuce's avatar
deuce committed
472
 * vstatlock needs to be held.
473
 */
deuce's avatar
deuce committed
474
static int bitmap_draw_one_char(unsigned int xpos, unsigned int ypos)
deuce's avatar
deuce committed
475
{
476 477
	uint32_t fg;
	uint32_t bg;
deuce's avatar
deuce committed
478 479
	int		xoffset=(xpos-1)*vstat.charwidth;
	int		yoffset=(ypos-1)*vstat.charheight;
480 481 482 483
	int		x;
	int		y;
	int		fontoffset;
	unsigned char *this_font;
deuce's avatar
deuce committed
484
	WORD	sch;
deuce's avatar
deuce committed
485
	struct vstat_vmem *vmem_ptr;
486
	BOOL	draw_fg = TRUE;
deuce's avatar
deuce committed
487

488 489
	if(!bitmap_initialized) {
		return(-1);
deuce's avatar
deuce committed
490 491
	}

deuce's avatar
deuce committed
492
	vmem_ptr = vstat.vmem;
493

494 495
	if(!vmem_ptr) {
		return(-1);
496 497
	}

498 499
	pthread_mutex_lock(&screena.screenlock);
	pthread_mutex_lock(&screenb.screenlock);
deuce's avatar
deuce committed
500

501 502 503 504
	if ((xoffset + vstat.charwidth > screena.screenwidth) || (yoffset + vstat.charheight > screena.screenheight) ||
	    (xoffset + vstat.charwidth > screenb.screenwidth) || (yoffset + vstat.charheight > screenb.screenheight)) {
		pthread_mutex_unlock(&screenb.screenlock);
		pthread_mutex_unlock(&screena.screenlock);
505 506
		return(-1);
	}
deuce's avatar
deuce committed
507

508 509 510
	if((!screena.screen) || (!screenb.screen)) {
		pthread_mutex_unlock(&screenb.screenlock);
		pthread_mutex_unlock(&screena.screenlock);
511 512
		return(-1);
	}
deuce's avatar
deuce committed
513

514 515 516
	sch=vmem_ptr->vmem[(ypos-1)*cio_textinfo.screenwidth+(xpos-1)].legacy_attr << 8 | vmem_ptr->vmem[(ypos-1)*cio_textinfo.screenwidth+(xpos-1)].ch;
	fg = vmem_ptr->vmem[(ypos-1)*cio_textinfo.screenwidth+(xpos-1)].fg;
	bg = vmem_ptr->vmem[(ypos-1)*cio_textinfo.screenwidth+(xpos-1)].bg;
517

518 519 520
	if (vstat.forced_font) {
		this_font = vstat.forced_font;
	}
deuce's avatar
deuce committed
521
	else {
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
		if (current_font[0] == -1)
			this_font = font[0];
		else {
			switch (vstat.charheight) {
				case 8:
					this_font = (unsigned char *)conio_fontdata[vmem_ptr->vmem[(ypos-1)*cio_textinfo.screenwidth+(xpos-1)].font].eight_by_eight;
					break;
				case 14:
					this_font = (unsigned char *)conio_fontdata[vmem_ptr->vmem[(ypos-1)*cio_textinfo.screenwidth+(xpos-1)].font].eight_by_fourteen;
					break;
				case 16:
					this_font = (unsigned char *)conio_fontdata[vmem_ptr->vmem[(ypos-1)*cio_textinfo.screenwidth+(xpos-1)].font].eight_by_sixteen;
					break;
				default:
					pthread_mutex_unlock(&screenb.screenlock);
					pthread_mutex_unlock(&screena.screenlock);
					return(-1);
			}
deuce's avatar
deuce committed
540
		}
541
	}
542 543
	if (this_font == NULL)
		this_font = font[0];
544
	fontoffset=(sch & 0xff) * (vstat.charheight * ((vstat.charwidth + 7) / 8));
545

546
	draw_fg = ((!(sch & 0x8000)) || vstat.no_blink);
deuce's avatar
deuce committed
547
	for(y=0; y<vstat.charheight; y++) {
548
		for(x=0; x<vstat.charwidth; x++) {
549 550 551 552
			if(this_font[fontoffset] & (0x80 >> (x & 7)) && draw_fg) {
				if (screena.screen[PIXEL_OFFSET(screena, xoffset + x, yoffset + y)] != fg) {
					screena.update_pixels = 1;
					screena.screen[PIXEL_OFFSET(screena, xoffset + x, yoffset + y)] = fg;
553
				}
554
			}
555 556 557 558
			else {
				if (screena.screen[PIXEL_OFFSET(screena, xoffset + x, yoffset + y)] != bg) {
					screena.update_pixels = 1;
					screena.screen[PIXEL_OFFSET(screena, xoffset + x, yoffset + y)] = bg;
559
				}
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
			}
			if(this_font[fontoffset] & (0x80 >> (x & 7))) {
				if (screenb.screen[PIXEL_OFFSET(screenb, xoffset + x, yoffset + y)]!=fg) {
					screenb.update_pixels = 1;
					screenb.screen[PIXEL_OFFSET(screenb, xoffset + x, yoffset + y)]=fg;
				}
			}
			else {
				if (screenb.screen[PIXEL_OFFSET(screenb, xoffset+x, yoffset+y)]!=bg) {
					screenb.update_pixels = 1;
					screenb.screen[PIXEL_OFFSET(screenb, xoffset+x, yoffset+y)]=bg;
				}
			}
			if ((x & 0x07) == 7)
				fontoffset++;
deuce's avatar
deuce committed
575
		}
576 577
		if (x & 0x07)
			fontoffset++;
deuce's avatar
deuce committed
578
	}
579 580
	pthread_mutex_unlock(&screenb.screenlock);
	pthread_mutex_unlock(&screena.screenlock);
deuce's avatar
deuce committed
581

582
	return(0);
deuce's avatar
deuce committed
583 584
}

585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
/***********************************************************/
/* These functions get called from the blinker thread only */
/***********************************************************/

static void cb_flush(void)
{
	pthread_mutex_lock(&callbacks.lock);
	if (callbacks.rects) {
		callbacks.flush();
		callbacks.rects = 0;
	}
	pthread_mutex_unlock(&callbacks.lock);
}

static int check_redraw(void)
{
	int ret;

	pthread_mutex_lock(&vstatlock);
	ret = force_redraws;
	force_redraws = 0;
	pthread_mutex_unlock(&vstatlock);
	return ret;
}

/* Blinker Thread */
static void blinker_thread(void *data)
{
	void *rect;
	int count=0;
615 616 617
	int curs_changed;
	int blink_changed;
	struct bitmap_screen *screen;
618 619 620

	SetThreadName("Blinker");
	while(1) {
621 622
		curs_changed = 0;
		blink_changed = 0;
623 624
		do {
			SLEEP(10);
625 626
			screen = current_screen();
		} while (screen->screen == NULL);
627
		count++;
628 629
		if (count==25) {
			pthread_mutex_lock(&vstatlock);
630
			curs_changed = cursor_visible_locked();
631 632 633 634
			if(vstat.curs_blink)
				vstat.curs_blink=FALSE;
			else
				vstat.curs_blink=TRUE;
635
			curs_changed = (curs_changed != cursor_visible_locked());
636 637
			pthread_mutex_unlock(&vstatlock);
		}
638 639 640 641 642 643
		if(count==50) {
			pthread_mutex_lock(&vstatlock);
			if(vstat.blink)
				vstat.blink=FALSE;
			else
				vstat.blink=TRUE;
644 645
			blink_changed = 1;
			curs_changed = cursor_visible_locked();
646 647 648 649
			if(vstat.curs_blink)
				vstat.curs_blink=FALSE;
			else
				vstat.curs_blink=TRUE;
650
			curs_changed = (curs_changed != cursor_visible_locked());
651 652 653 654 655 656
			count=0;
			pthread_mutex_unlock(&vstatlock);
		}
		/* Lock out ciolib while we handle shit */
		pthread_mutex_lock(&blinker_lock);
		if (check_redraw()) {
deuce's avatar
deuce committed
657
			if (update_from_vmem(TRUE))
658 659 660
				request_redraw();
		}
		else {
661
			if (count==0)
deuce's avatar
deuce committed
662 663
				if (update_from_vmem(FALSE))
					request_redraw();
664
		}
665 666 667 668 669
		pthread_mutex_lock(&screen->screenlock);
		if (screen->update_pixels || curs_changed || blink_changed) {
			rect = get_full_rectangle_locked(screen);
			screen->update_pixels = 0;
			pthread_mutex_unlock(&screen->screenlock);
670
			cb_drawrect(rect);
671
		}
Deucе's avatar
Deucе committed
672 673 674 675
		else {
			if (force_cursor) {
				rect = get_full_rectangle_locked(screen);
			}
676
			pthread_mutex_unlock(&screen->screenlock);
Deucе's avatar
Deucе committed
677 678 679 680 681
			if (force_cursor) {
				cb_drawrect(rect);
				force_cursor = 0;
			}
		}
682 683 684 685 686
		cb_flush();
		pthread_mutex_unlock(&blinker_lock);
	}
}

687 688 689 690 691 692 693 694
static __inline struct bitmap_screen *current_screen_locked(void)
{
	if (vstat.blink)
		return &screena;
	return(&screenb);
}

static __inline struct bitmap_screen *current_screen(void)
695
{
696 697 698 699 700
	struct bitmap_screen *ret;

	pthread_mutex_lock(&vstatlock);
	ret = current_screen_locked();
	pthread_mutex_unlock(&vstatlock);
701 702 703
	return(ret);
}

704
/*
705 706 707
 * Updates any changed cells... blinking, modified flags, and the cursor
 * Is also used (with force = TRUE) to completely redraw the screen from
 * vmem (such as in the case of a font load).
708
 */
deuce's avatar
deuce committed
709
static int update_from_vmem(int force)
deuce's avatar
deuce committed
710
{
deuce's avatar
deuce committed
711 712 713
	static struct video_stats vs;
	struct vstat_vmem *vmem_ptr;
	int x,y,width,height;
714
	unsigned int pos;
deuce's avatar
deuce committed
715

716 717
	int bright_attr_changed=0;
	int blink_attr_changed=0;
deuce's avatar
deuce committed
718

719
	if(!bitmap_initialized)
720
		return(-1);
deuce's avatar
deuce committed
721

deuce's avatar
deuce committed
722 723
	width=cio_textinfo.screenwidth;
	height=cio_textinfo.screenheight;
deuce's avatar
deuce committed
724

725
	pthread_mutex_lock(&vstatlock);
726 727

	if (vstat.vmem == NULL) {
728
		pthread_mutex_unlock(&vstatlock);
729
		return -1;
deuce's avatar
deuce committed
730
	}
731

732
	if(vstat.vmem->vmem == NULL) {
733
		pthread_mutex_unlock(&vstatlock);
734
		return -1;
735
	}
736

deuce's avatar
deuce committed
737
	/* If we change window size, redraw everything */
738
	if(vs.cols!=vstat.cols || vs.rows != vstat.rows) {
739 740 741 742
		/* Force a full redraw */
		width=vstat.cols;
		height=vstat.rows;
		force=1;
743
	}
744

745 746 747 748 749
	/* Did the meaning of the blink bit change? */
	if (vstat.bright_background != vs.bright_background ||
			vstat.no_blink != vs.no_blink ||
			vstat.blink_altcharset != vs.blink_altcharset)
	    blink_attr_changed = 1;
deuce's avatar
deuce committed
750

751 752 753 754 755
	/* Did the meaning of the bright bit change? */
	if (vstat.no_bright != vs.no_bright ||
			vstat.bright_altcharset != vs.bright_altcharset)
		bright_attr_changed = 1;

deuce's avatar
deuce committed
756
	/* Get vmem pointer */
757 758 759 760 761 762 763 764 765 766 767 768
	vmem_ptr = get_vmem(&vstat);

	/* 
	 * Now we go through each character seeing if it's changed (or force is set)
	 * We combine updates into rectangles by lines...
	 * 
	 * First, in the same line, we build this_rect.
	 * At the end of the line, if this_rect is the same width as the screen,
	 * we add it to last_rect.
	 */

	for(y=0;y<height;y++) {
deuce's avatar
deuce committed
769
		pos=y*vstat.cols;
770 771
		for(x=0;x<width;x++) {
			/* Last this char been updated? */
772 773 774 775 776
			if(force										/* Forced */
			    || ((vstat.vmem->vmem[pos].legacy_attr & 0x80) && blink_attr_changed)
			    || ((vstat.vmem->vmem[pos].legacy_attr & 0x08) && bright_attr_changed))	/* Bright char */
			    {
				bitmap_draw_one_char(x+1,y+1);
deuce's avatar
deuce committed
777
			}
778
			pos++;
779
		}
deuce's avatar
deuce committed
780
	}
781 782
	release_vmem(vmem_ptr);

783
	pthread_mutex_unlock(&vstatlock);
deuce's avatar
deuce committed
784

785 786 787
	vs = vstat;

	return(0);
deuce's avatar
deuce committed
788 789
}

790 791
/*************************************/

792 793 794 795
/**********************/
/* Called from ciolib */
/**********************/
int bitmap_puttext(int sx, int sy, int ex, int ey, void *fill)
796
{
797 798
	int x, y;
	int ret = 1;
799
	uint16_t *buf = fill;
800
	struct vstat_vmem *vmem_ptr;
801 802 803

	pthread_mutex_lock(&blinker_lock);

804 805 806 807 808
	pthread_mutex_lock(&vstatlock);
	vmem_ptr = get_vmem(&vstat);
	for(y=sy-1;y<ey;y++) {
		for(x=sx-1;x<ex;x++) {
			set_vmem_cell(vmem_ptr, y*cio_textinfo.screenwidth+x, *(buf++), 0x00ffffff, 0x00ffffff);
809
			bitmap_draw_one_char(x+1, y+1);
810 811 812 813
		}
	}
	release_vmem(vmem_ptr);
	pthread_mutex_unlock(&vstatlock);
814
	pthread_mutex_unlock(&blinker_lock);
815
	return ret;
816 817
}

818
int bitmap_vmem_puttext(int sx, int sy, int ex, int ey, struct vmem_cell *fill)
819
{
820
	int ret;
821 822 823

	if(!bitmap_initialized)
		return(0);
824 825

	pthread_mutex_lock(&blinker_lock);
826
	ret = bitmap_vmem_puttext_locked(sx, sy, ex, ey, fill);
827
	pthread_mutex_unlock(&blinker_lock);
828 829

	return ret;
830 831
}

832
int bitmap_vmem_gettext(int sx, int sy, int ex, int ey, struct vmem_cell *fill)
deuce's avatar
deuce committed
833
{
834 835
	int x,y;
	struct vstat_vmem *vmem_ptr;
deuce's avatar
deuce committed
836

837
	if(!bitmap_initialized)
838
		return(0);
deuce's avatar
deuce committed
839

840 841 842 843 844 845 846 847 848 849
	if(		   sx < 1
			|| sy < 1
			|| ex < 1
			|| ey < 1
			|| sx > ex
			|| sy > ey
			|| ex > cio_textinfo.screenwidth
			|| ey > cio_textinfo.screenheight
			|| fill==NULL) {
		return(0);
deuce's avatar
deuce committed
850 851
	}

852
	pthread_mutex_lock(&blinker_lock);
853
	pthread_mutex_lock(&vstatlock);
854 855
	vmem_ptr = get_vmem(&vstat);
	for(y=sy-1;y<ey;y++) {
856 857
		for(x=sx-1;x<ex;x++)
			memcpy(fill++, &vmem_ptr->vmem[y*cio_textinfo.screenwidth+x], sizeof(*fill));
deuce's avatar
deuce committed
858
	}
859
	release_vmem(vmem_ptr);
860
	pthread_mutex_unlock(&vstatlock);
861
	pthread_mutex_unlock(&blinker_lock);
862
	return(1);
deuce's avatar
deuce committed
863 864 865 866
}

void bitmap_gotoxy(int x, int y)
{
867 868
	if(!bitmap_initialized)
		return;
869
	/* Move cursor location */
870
	pthread_mutex_lock(&blinker_lock);
Deucе's avatar
Deucе committed
871 872 873 874 875 876 877 878 879
	pthread_mutex_lock(&vstatlock);
	if (vstat.curs_col != x + cio_textinfo.winleft - 1 || vstat.curs_row != y + cio_textinfo.wintop - 1) {
		cio_textinfo.curx=x;
		cio_textinfo.cury=y;
		vstat.curs_col = x + cio_textinfo.winleft - 1;
		vstat.curs_row = y + cio_textinfo.wintop - 1;
		force_cursor = 1;
	}
	pthread_mutex_unlock(&vstatlock);
880
	pthread_mutex_unlock(&blinker_lock);
deuce's avatar
deuce committed
881 882
}

883
void bitmap_setcursortype(int type)
deuce's avatar
deuce committed
884
{
885 886
	if(!bitmap_initialized)
		return;
887
	pthread_mutex_lock(&blinker_lock);
888
	pthread_mutex_lock(&vstatlock);
889 890 891 892 893 894 895 896 897 898 899 900 901
	switch(type) {
		case _NOCURSOR:
			vstat.curs_start=0xff;
			vstat.curs_end=0;
			break;
		case _SOLIDCURSOR:
			vstat.curs_start=0;
			vstat.curs_end=vstat.charheight-1;
			break;
		default:
		    vstat.curs_start = vstat.default_curs_start;
		    vstat.curs_end = vstat.default_curs_end;
			break;
deuce's avatar
deuce committed
902
	}
903
	pthread_mutex_unlock(&vstatlock);
904
	pthread_mutex_unlock(&blinker_lock);
905
}
906

907 908 909 910 911
int bitmap_setfont(int font, int force, int font_num)
{
	int changemode=0;
	int	newmode=-1;
	struct text_info ti;
912
	struct vmem_cell	*old;
913 914
	int		ow,oh;
	int		row,col;
915
	struct vmem_cell	*new;
916
	int		attr;
917 918
	struct vmem_cell	*pold;
	struct vmem_cell	*pnew;
919

920
	if(!bitmap_initialized)
921 922 923
		return(0);
	if(font < 0 || font>(sizeof(conio_fontdata)/sizeof(struct conio_font_data_struct)-2))
		return(0);
924

925 926 927 928 929 930
	if(conio_fontdata[font].eight_by_sixteen!=NULL)
		newmode=C80;
	else if(conio_fontdata[font].eight_by_fourteen!=NULL)
		newmode=C80X28;
	else if(conio_fontdata[font].eight_by_eight!=NULL)
		newmode=C80X50;
931

932
	pthread_mutex_lock(&blinker_lock);
933
	pthread_mutex_lock(&vstatlock);
934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959
	switch(vstat.charheight) {
		case 8:
			if(conio_fontdata[font].eight_by_eight==NULL) {
				if(!force)
					goto error_return;
				else
					changemode=1;
			}
			break;
		case 14:
			if(conio_fontdata[font].eight_by_fourteen==NULL) {
				if(!force)
					goto error_return;
				else
					changemode=1;
			}
			break;
		case 16:
			if(conio_fontdata[font].eight_by_sixteen==NULL) {
				if(!force)
					goto error_return;
				else
					changemode=1;
			}
			break;
	}
960
	if(changemode && (newmode==-1 || font_num > 1))
961 962 963 964 965 966 967 968 969 970 971 972 973
		goto error_return;
	switch(font_num) {
		case 0:
			default_font=font;
			/* Fall-through */
		case 1:
			current_font[0]=font;
			break;
		case 2:
		case 3:
		case 4:
			current_font[font_num-1]=font;
			break;
974
	}
deuce's avatar
deuce committed
975

976 977
	if(changemode) {
		gettextinfo(&ti);
deuce's avatar
deuce committed
978

979 980 981
		attr=ti.attribute;
		ow=ti.screenwidth;
		oh=ti.screenheight;