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 43.2 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

#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"
Deucе's avatar
Deucе committed
14
#include "scale.h"
deuce's avatar
deuce committed
15 16 17 18 19 20 21 22 23 24

#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
25
#include "bitmap_con.h"
deuce's avatar
deuce committed
26

27
static uint32_t palette[65536];
28

29 30
#if 0

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

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

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

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

49
static int dbg_pthread_mutex_trylock(pthread_mutex_t *lptr, unsigned line)
50 51 52 53 54 55 56 57 58 59 60 61 62 63
{
	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

64 65 66
/* Structs */

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

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

81
/* Static globals */
82

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

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

99
/* Exported globals */
100

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

104
/* Forward declarations */
105

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

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

126
static int bitmap_loadfont_locked(char *filename)
127
{
128 129
	static char current_filename[MAX_PATH];
	unsigned int fontsize;
Deucе's avatar
Deucе committed
130
	int fdw;
131 132 133 134
	int fw;
	int fh;
	int i;
	FILE	*fontfile=NULL;
135

136
	if(!bitmap_initialized)
137
		return(0);
138 139 140 141 142 143 144 145 146 147 148 149 150
	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)
151
		return(0);
152

153 154 155 156 157 158 159 160
	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];
	}
161

162
	fh=vstat.charheight;
Deucе's avatar
Deucе committed
163 164
	fdw = vstat.charwidth - (vstat.flags & VIDMODES_FLAG_EXPAND) ? 1 : 0;
	fw = fdw / 8 + (fdw % 8 ? 1 : 0);
165

166 167 168 169 170 171 172
	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;
173
	}
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194

	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;
Deucе's avatar
Deucе committed
195
		switch(fdw) {
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 232 233 234
			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;
		}
235 236
	}

237
    return(1);
238 239 240 241 242 243

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

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

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

255
static int bitmap_vmem_puttext_locked(int sx, int sy, int ex, int ey, struct vmem_cell *fill)
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
{
	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++) {
280
			memcpy(&vmem_ptr->vmem[y*cio_textinfo.screenwidth+x], fill++, sizeof(*fill));
281
			bitmap_draw_one_char(x+1, y+1);
282 283 284 285 286 287 288
		}
	}
	release_vmem(vmem_ptr);
	pthread_mutex_unlock(&vstatlock);
	return(1);
}

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

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

296 297 298 299 300 301
	altfont = (cell>>11 & 0x01) | ((cell>>14) & 0x02);
	if (!vstat.bright_altcharset)
		altfont &= ~0x01;
	if (!vstat.blink_altcharset)
		altfont &= ~0x02;
	font=current_font[altfont];
302 303
	if (font == -99)
		font = default_font;
304 305 306 307 308 309 310 311
	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;
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
}

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];

329
	return 1;
330 331 332 333 334 335
}

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

336 337
static int
cursor_visible_locked(void)
338
{
339 340 341 342 343 344 345 346
	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;
347
	}
348
	return 1;
349 350
}

351
static void	cb_drawrect(struct rectlist *data)
352
{
353 354 355 356
	int x, y;
	uint32_t *pixel;
	uint32_t cv;

357 358
	if (data == NULL)
		return;
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
	/*
	 * 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);
380
	pthread_mutex_lock(&callbacks.lock);
381
	callbacks.drawrect(data);
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
	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);
}
397

398 399 400
/*
 * Called with the screen lock held
 */
401
static struct rectlist *alloc_full_rect(struct bitmap_screen *screen)
402 403 404
{
	struct rectlist * ret;

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

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

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

446
static struct rectlist *get_full_rectangle_locked(struct bitmap_screen *screen)
447
{
448
	size_t i;
deuce's avatar
deuce committed
449
	struct rectlist *rect;
450

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

463 464 465 466
static void memset_u32(void *buf, uint32_t u, size_t len)
{
	size_t i;
	char *cbuf = buf;
467

468 469 470
	for (i = 0; i < len; i++) {
		memcpy(cbuf, &u, sizeof(uint32_t));
		cbuf += sizeof(uint32_t);
471 472 473
	}
}

474
/*
deuce's avatar
deuce committed
475
 * vstatlock needs to be held.
476
 */
deuce's avatar
deuce committed
477
static int bitmap_draw_one_char(unsigned int xpos, unsigned int ypos)
deuce's avatar
deuce committed
478
{
479 480
	uint32_t fg;
	uint32_t bg;
Deucе's avatar
Deucе committed
481
	int fdw;
deuce's avatar
deuce committed
482 483
	int		xoffset=(xpos-1)*vstat.charwidth;
	int		yoffset=(ypos-1)*vstat.charheight;
484
	int		x;
Deucе's avatar
Deucе committed
485 486
	int		fdx;
	uint8_t fb = 0;
487 488 489
	int		y;
	int		fontoffset;
	unsigned char *this_font;
deuce's avatar
deuce committed
490
	WORD	sch;
deuce's avatar
deuce committed
491
	struct vstat_vmem *vmem_ptr;
492
	BOOL	draw_fg = TRUE;
deuce's avatar
deuce committed
493

494 495
	if(!bitmap_initialized) {
		return(-1);
deuce's avatar
deuce committed
496 497
	}

deuce's avatar
deuce committed
498
	vmem_ptr = vstat.vmem;
499

500 501
	if(!vmem_ptr) {
		return(-1);
502 503
	}

504 505
	pthread_mutex_lock(&screena.screenlock);
	pthread_mutex_lock(&screenb.screenlock);
deuce's avatar
deuce committed
506

507 508 509 510
	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);
511 512
		return(-1);
	}
deuce's avatar
deuce committed
513

514 515 516
	if((!screena.screen) || (!screenb.screen)) {
		pthread_mutex_unlock(&screenb.screenlock);
		pthread_mutex_unlock(&screena.screenlock);
517 518
		return(-1);
	}
deuce's avatar
deuce committed
519

520 521 522
	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;
523

524 525 526
	if (vstat.forced_font) {
		this_font = vstat.forced_font;
	}
deuce's avatar
deuce committed
527
	else {
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
		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
546
		}
547
	}
548 549
	if (this_font == NULL)
		this_font = font[0];
Deucе's avatar
Deucе committed
550 551
	fdw = vstat.charwidth - (vstat.flags & VIDMODES_FLAG_EXPAND) ? 1 : 0;
	fontoffset=(sch & 0xff) * (vstat.charheight * ((fdw + 7) / 8));
552

553
	draw_fg = ((!(sch & 0x8000)) || vstat.no_blink);
deuce's avatar
deuce committed
554
	for(y=0; y<vstat.charheight; y++) {
555
		for(x=0; x<vstat.charwidth; x++) {
Deucе's avatar
Deucе committed
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
			fdx = x;
			fb = this_font[fontoffset];
			if ((x & 0x07) == 7)
				fontoffset++;
			if (vstat.flags & VIDMODES_FLAG_EXPAND) {
				if (x == vstat.charwidth - 1) {
					fontoffset--;
					fdx--;
					if (!(vstat.flags & VIDMODES_FLAG_LINE_GRAPHICS_EXPAND)) {
						fb = 0;
					}
					else if ((sch & 0xff) >= 0xC0 && (sch & 0xff) <= 0xDF) {
						fb = this_font[fontoffset];
					}
					else
						fb = 0;
					
				}
			}

			if(fb & (0x80 >> (fdx & 7)) && draw_fg) {
577 578 579
				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;
580
				}
581
			}
582 583 584 585
			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;
586
				}
587
			}
Deucе's avatar
Deucе committed
588 589

			if(fb & (0x80 >> (fdx & 7))) {
590 591 592 593 594 595 596 597 598 599 600
				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;
				}
			}
Deucе's avatar
Deucе committed
601

deuce's avatar
deuce committed
602
		}
603 604
		if (x & 0x07)
			fontoffset++;
deuce's avatar
deuce committed
605
	}
606 607
	pthread_mutex_unlock(&screenb.screenlock);
	pthread_mutex_unlock(&screena.screenlock);
deuce's avatar
deuce committed
608

609
	return(0);
deuce's avatar
deuce committed
610 611
}

612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
/***********************************************************/
/* 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;
642 643 644
	int curs_changed;
	int blink_changed;
	struct bitmap_screen *screen;
645 646 647

	SetThreadName("Blinker");
	while(1) {
648 649
		curs_changed = 0;
		blink_changed = 0;
650 651
		do {
			SLEEP(10);
652 653
			screen = current_screen();
		} while (screen->screen == NULL);
654
		count++;
655 656
		if (count==25) {
			pthread_mutex_lock(&vstatlock);
657
			curs_changed = cursor_visible_locked();
658 659 660 661
			if(vstat.curs_blink)
				vstat.curs_blink=FALSE;
			else
				vstat.curs_blink=TRUE;
662
			curs_changed = (curs_changed != cursor_visible_locked());
663 664
			pthread_mutex_unlock(&vstatlock);
		}
665 666 667 668 669 670
		if(count==50) {
			pthread_mutex_lock(&vstatlock);
			if(vstat.blink)
				vstat.blink=FALSE;
			else
				vstat.blink=TRUE;
671 672
			blink_changed = 1;
			curs_changed = cursor_visible_locked();
673 674 675 676
			if(vstat.curs_blink)
				vstat.curs_blink=FALSE;
			else
				vstat.curs_blink=TRUE;
677
			curs_changed = (curs_changed != cursor_visible_locked());
678 679 680 681 682 683
			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
684
			if (update_from_vmem(TRUE))
685 686 687
				request_redraw();
		}
		else {
688
			if (count==0)
deuce's avatar
deuce committed
689 690
				if (update_from_vmem(FALSE))
					request_redraw();
691
		}
692 693 694 695 696
		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);
697
			cb_drawrect(rect);
698
		}
Deucе's avatar
Deucе committed
699 700 701 702
		else {
			if (force_cursor) {
				rect = get_full_rectangle_locked(screen);
			}
703
			pthread_mutex_unlock(&screen->screenlock);
Deucе's avatar
Deucе committed
704 705 706 707 708
			if (force_cursor) {
				cb_drawrect(rect);
				force_cursor = 0;
			}
		}
709 710 711 712 713
		cb_flush();
		pthread_mutex_unlock(&blinker_lock);
	}
}

714 715 716 717 718 719 720 721
static __inline struct bitmap_screen *current_screen_locked(void)
{
	if (vstat.blink)
		return &screena;
	return(&screenb);
}

static __inline struct bitmap_screen *current_screen(void)
722
{
723 724 725 726 727
	struct bitmap_screen *ret;

	pthread_mutex_lock(&vstatlock);
	ret = current_screen_locked();
	pthread_mutex_unlock(&vstatlock);
728 729 730
	return(ret);
}

731
/*
732 733 734
 * 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).
735
 */
deuce's avatar
deuce committed
736
static int update_from_vmem(int force)
deuce's avatar
deuce committed
737
{
deuce's avatar
deuce committed
738 739 740
	static struct video_stats vs;
	struct vstat_vmem *vmem_ptr;
	int x,y,width,height;
741
	unsigned int pos;
deuce's avatar
deuce committed
742

743 744
	int bright_attr_changed=0;
	int blink_attr_changed=0;
deuce's avatar
deuce committed
745

746
	if(!bitmap_initialized)
747
		return(-1);
deuce's avatar
deuce committed
748

deuce's avatar
deuce committed
749 750
	width=cio_textinfo.screenwidth;
	height=cio_textinfo.screenheight;
deuce's avatar
deuce committed
751

752
	pthread_mutex_lock(&vstatlock);
753 754

	if (vstat.vmem == NULL) {
755
		pthread_mutex_unlock(&vstatlock);
756
		return -1;
deuce's avatar
deuce committed
757
	}
758

759
	if(vstat.vmem->vmem == NULL) {
760
		pthread_mutex_unlock(&vstatlock);
761
		return -1;
762
	}
763

deuce's avatar
deuce committed
764
	/* If we change window size, redraw everything */
765
	if(vs.cols!=vstat.cols || vs.rows != vstat.rows) {
766 767 768 769
		/* Force a full redraw */
		width=vstat.cols;
		height=vstat.rows;
		force=1;
770
	}
771

772 773 774 775 776
	/* 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
777

778 779 780 781 782
	/* 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
783
	/* Get vmem pointer */
784 785 786 787 788 789 790 791 792 793 794 795
	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
796
		pos=y*vstat.cols;
797 798
		for(x=0;x<width;x++) {
			/* Last this char been updated? */
799 800 801 802 803
			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
804
			}
805
			pos++;
806
		}
deuce's avatar
deuce committed
807
	}
808 809
	release_vmem(vmem_ptr);

810
	pthread_mutex_unlock(&vstatlock);
deuce's avatar
deuce committed
811

812 813 814
	vs = vstat;

	return(0);
deuce's avatar
deuce committed
815 816
}

817 818
/*************************************/

819 820 821 822
/**********************/
/* Called from ciolib */
/**********************/
int bitmap_puttext(int sx, int sy, int ex, int ey, void *fill)
823
{
824 825
	int x, y;
	int ret = 1;
826
	uint16_t *buf = fill;
827
	struct vstat_vmem *vmem_ptr;
828 829 830

	pthread_mutex_lock(&blinker_lock);

831 832 833 834 835
	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);
836
			bitmap_draw_one_char(x+1, y+1);
837 838 839 840
		}
	}
	release_vmem(vmem_ptr);
	pthread_mutex_unlock(&vstatlock);
841
	pthread_mutex_unlock(&blinker_lock);
842
	return ret;
843 844
}

845
int bitmap_vmem_puttext(int sx, int sy, int ex, int ey, struct vmem_cell *fill)
846
{
847
	int ret;
848 849 850

	if(!bitmap_initialized)
		return(0);
851 852

	pthread_mutex_lock(&blinker_lock);
853
	ret = bitmap_vmem_puttext_locked(sx, sy, ex, ey, fill);
854
	pthread_mutex_unlock(&blinker_lock);
855 856

	return ret;
857 858
}

859
int bitmap_vmem_gettext(int sx, int sy, int ex, int ey, struct vmem_cell *fill)
deuce's avatar
deuce committed
860
{
861 862
	int x,y;
	struct vstat_vmem *vmem_ptr;
deuce's avatar
deuce committed
863

864
	if(!bitmap_initialized)
865
		return(0);
deuce's avatar
deuce committed
866

867 868 869 870 871 872 873 874 875 876
	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
877 878
	}

879
	pthread_mutex_lock(&blinker_lock);
880
	pthread_mutex_lock(&vstatlock);
881 882
	vmem_ptr = get_vmem(&vstat);
	for(y=sy-1;y<ey;y++) {
883 884
		for(x=sx-1;x<ex;x++)
			memcpy(fill++, &vmem_ptr->vmem[y*cio_textinfo.screenwidth+x], sizeof(*fill));
deuce's avatar
deuce committed
885
	}
886
	release_vmem(vmem_ptr);
887
	pthread_mutex_unlock(&vstatlock);
888
	pthread_mutex_unlock(&blinker_lock);
889
	return(1);
deuce's avatar
deuce committed
890 891 892 893
}

void bitmap_gotoxy(int x, int y)
{
894 895
	if(!bitmap_initialized)
		return;
896
	/* Move cursor location */
897
	pthread_mutex_lock(&blinker_lock);
Deucе's avatar
Deucе committed
898 899 900 901 902 903 904 905 906
	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);
907
	pthread_mutex_unlock(&blinker_lock);
deuce's avatar
deuce committed
908 909
}

910
void bitmap_setcursortype(int type)
deuce's avatar
deuce committed
911
{
912 913
	if(!bitmap_initialized)
		return;
914
	pthread_mutex_lock(&blinker_lock);
915
	pthread_mutex_lock(&vstatlock);
916 917 918 919 920 921 922 923 924 925 926 927 928
	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
929
	}
930
	pthread_mutex_unlock(&vstatlock);
931
	pthread_mutex_unlock(&blinker_lock);
932
}
933

934 935 936 937 938
int bitmap_setfont(int font, int force, int font_num)
{
	int changemode=0;
	int	newmode=-1;
	struct text_info ti;
939
	struct vmem_cell	*old;
940 941
	int		ow,oh;
	int		row,col;
942
	struct vmem_cell	*new;
943
	int		attr;
944 945
	struct vmem_cell	*pold;
	struct vmem_cell	*pnew;
946

947
	if(!bitmap_initialized)
948 949 950
		return(0);
	if(font < 0 || font>(sizeof(conio_fontdata)/sizeof(struct conio_font_data_struct)-2))
		return(0);
951

952 953 954 955 956 957
	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;
958

959
	pthread_mutex_lock(&blinker_lock);
960
	pthread_mutex_lock(&vstatlock);