1 /* drivers.c
2  * (c) 2002 Mikulas Patocka
3  * This file is a part of the Links program, released under GPL
4  */
5 
6 #include "cfg.h"
7 
8 #ifdef G
9 
10 #include "links.h"
11 
12 int F = 0;
13 
14 struct graphics_driver *drv = NULL;
15 
16 #ifdef GRDRV_X
17 extern struct graphics_driver x_driver;
18 #endif
19 #ifdef GRDRV_SVGALIB
20 extern struct graphics_driver svga_driver;
21 #endif
22 #ifdef GRDRV_FB
23 extern struct graphics_driver fb_driver;
24 #endif
25 #ifdef GRDRV_DIRECTFB
26 extern struct graphics_driver directfb_driver;
27 #endif
28 #ifdef GRDRV_PMSHELL
29 extern struct graphics_driver pmshell_driver;
30 #endif
31 #ifdef GRDRV_ATHEOS
32 extern struct graphics_driver atheos_driver;
33 #endif
34 #ifdef GRDRV_HAIKU
35 extern struct graphics_driver haiku_driver;
36 #endif
37 #ifdef GRDRV_GRX
38 extern struct graphics_driver grx_driver;
39 #endif
40 #ifdef GRDRV_SDL
41 extern struct graphics_driver sdl_driver;
42 #endif
43 
44 /*
45  * On SPAD you must test first svgalib and then X (because X test is slow).
46  * On other systems you must test first X and then svgalib (because svgalib
47  *	would work in X too and it's undesirable).
48  */
49 
50 static struct graphics_driver *graphics_drivers[] = {
51 #ifdef GRDRV_PMSHELL
52 	&pmshell_driver,
53 #endif
54 #ifdef GRDRV_ATHEOS
55 	&atheos_driver,
56 #endif
57 #ifdef GRDRV_HAIKU
58 	&haiku_driver,
59 #endif
60 #ifndef SPAD
61 #ifdef GRDRV_X
62 	&x_driver,
63 #endif
64 #endif
65 #ifdef GRDRV_FB
66 	/* use FB before DirectFB because DirectFB has bugs */
67 	&fb_driver,
68 #endif
69 #ifdef GRDRV_DIRECTFB
70 	&directfb_driver,
71 #endif
72 #ifdef GRDRV_SVGALIB
73 	&svga_driver,
74 #endif
75 #ifdef SPAD
76 #ifdef GRDRV_X
77 	&x_driver,
78 #endif
79 #endif
80 #ifdef GRDRV_GRX
81 	&grx_driver,
82 #endif
83 #ifdef GRDRV_SDL
84 	&sdl_driver,
85 #endif
86 	NULL
87 };
88 
89 #if 0
90 static unsigned char *list_graphics_drivers(void)
91 {
92 	unsigned char *d = init_str();
93 	int l = 0;
94 	struct graphics_driver **gd;
95 	for (gd = graphics_drivers; *gd; gd++) {
96 		if (l) add_chr_to_str(&d, &l, ' ');
97 		add_to_str(&d, &l, (*gd)->name);
98 	}
99 	return d;
100 }
101 #endif
102 
103 /* Driver je jednorazovy argument, kterej se preda grafickymu driveru, nikde se dal
104  * neuklada.  Param se skladuje v default_driver param a uklada se do konfiguraku. Pred
105  * ukoncenim grafickeho driveru se nastavi default_driver_param podle
106  * drv->get_driver_param.
107  */
init_graphics_driver(struct graphics_driver * gd,unsigned char * param,unsigned char * display)108 static unsigned char *init_graphics_driver(struct graphics_driver *gd, unsigned char *param, unsigned char *display)
109 {
110 	unsigned char *r;
111 	unsigned char *p = param;
112 	struct driver_param *dp = get_driver_param(gd->name);
113 	if (!param || !*param) p = dp->param;
114 	gd->param = dp;
115 	drv = gd;
116 	r = gd->init_driver(p,display);
117 	if (r) drv = NULL;
118 	else F = 1;
119 	return r;
120 }
121 
122 
add_graphics_drivers(unsigned char ** s,int * l)123 void add_graphics_drivers(unsigned char **s, int *l)
124 {
125 	struct graphics_driver **gd;
126 	for (gd = graphics_drivers; *gd; gd++) {
127 		if (gd != graphics_drivers) add_to_str(s, l, cast_uchar ", ");
128 		add_to_str(s, l, (*gd)->name);
129 	}
130 }
131 
init_graphics(unsigned char * driver,unsigned char * param,unsigned char * display)132 unsigned char *init_graphics(unsigned char *driver, unsigned char *param, unsigned char *display)
133 {
134 	unsigned char *s = init_str();
135 	int l = 0;
136 	struct graphics_driver **gd;
137 #if defined(GRDRV_PMSHELL) && defined(GRDRV_X)
138 	if (is_xterm()) {
139 		static unsigned char swapped = 0;
140 		if (!swapped) {
141 			for (gd = graphics_drivers; *gd; gd++) {
142 				if (*gd == &pmshell_driver) *gd = &x_driver;
143 				else if (*gd == &x_driver) *gd = &pmshell_driver;
144 			}
145 			swapped = 1;
146 		}
147 	}
148 #endif
149 	for (gd = graphics_drivers; *gd; gd++) {
150 		if (!driver || !*driver || !casestrcmp((*gd)->name, driver)) {
151 			unsigned char *r;
152 			if ((!driver || !*driver) && (*gd)->flags & GD_NOAUTO) continue;
153 			if (!(r = init_graphics_driver(*gd, param, display))) {
154 				mem_free(s);
155 				return NULL;
156 			}
157 			if (!l) {
158 				if (!driver || !*driver) add_to_str(&s, &l, cast_uchar "Could not initialize any graphics driver. Tried the following drivers:\n");
159 				else add_to_str(&s, &l, cast_uchar "Could not initialize graphics driver ");
160 			}
161 			add_to_str(&s, &l, (*gd)->name);
162 			add_to_str(&s, &l, cast_uchar ":\n");
163 			add_to_str(&s, &l, r);
164 			mem_free(r);
165 		}
166 	}
167 	if (!l) {
168 		add_to_str(&s, &l, cast_uchar "Unknown graphics driver ");
169 		if (driver) add_to_str(&s, &l, driver);
170 		add_to_str(&s, &l, cast_uchar ".\nThe following graphics drivers are supported:\n");
171 		add_graphics_drivers(&s, &l);
172 		add_to_str(&s, &l, cast_uchar "\n");
173 	}
174 	return s;
175 }
176 
shutdown_graphics(void)177 void shutdown_graphics(void)
178 {
179 	if (drv) {
180 		drv->shutdown_driver();
181 		drv = NULL;
182 		F = 0;
183 	}
184 }
185 
update_driver_param(void)186 void update_driver_param(void)
187 {
188 	if (drv) {
189 		struct driver_param *dp = drv->param;
190 		if (dp->param) mem_free(dp->param), dp->param = NULL;
191 		if (drv->get_driver_param)
192 			dp->param = stracpy(drv->get_driver_param());
193 		dp->nosave = 0;
194 	}
195 }
196 
g_kbd_codepage(struct graphics_driver * drv)197 int g_kbd_codepage(struct graphics_driver *drv)
198 {
199 	if (drv->param->kbd_codepage >= 0)
200 		return drv->param->kbd_codepage;
201 	return get_default_charset();
202 }
203 
do_rects_intersect(struct rect * r1,struct rect * r2)204 int do_rects_intersect(struct rect *r1, struct rect *r2)
205 {
206 	return (r1->x1 > r2->x1 ? r1->x1 : r2->x1) < (r1->x2 > r2->x2 ? r2->x2 : r1->x2) && (r1->y1 > r2->y1 ? r1->y1 : r2->y1) < (r1->y2 > r2->y2 ? r2->y2 : r1->y2);
207 }
208 
intersect_rect(struct rect * v,struct rect * r1,struct rect * r2)209 void intersect_rect(struct rect *v, struct rect *r1, struct rect *r2)
210 {
211 	v->x1 = r1->x1 > r2->x1 ? r1->x1 : r2->x1;
212 	v->x2 = r1->x2 > r2->x2 ? r2->x2 : r1->x2;
213 	v->y1 = r1->y1 > r2->y1 ? r1->y1 : r2->y1;
214 	v->y2 = r1->y2 > r2->y2 ? r2->y2 : r1->y2;
215 }
216 
unite_rect(struct rect * v,struct rect * r1,struct rect * r2)217 void unite_rect(struct rect *v, struct rect *r1, struct rect *r2)
218 {
219 	if (!is_rect_valid(r1)) {
220 		if (v != r2) memcpy(v, r2, sizeof(struct rect));
221 		return;
222 	}
223 	if (!is_rect_valid(r2)) {
224 		if (v != r1) memcpy(v, r1, sizeof(struct rect));
225 		return;
226 	}
227 	v->x1 = r1->x1 < r2->x1 ? r1->x1 : r2->x1;
228 	v->x2 = r1->x2 < r2->x2 ? r2->x2 : r1->x2;
229 	v->y1 = r1->y1 < r2->y1 ? r1->y1 : r2->y1;
230 	v->y2 = r1->y2 < r2->y2 ? r2->y2 : r1->y2;
231 }
232 
233 #define R_GR	8
234 
init_rect_set(void)235 struct rect_set *init_rect_set(void)
236 {
237 	struct rect_set *s;
238 	s = mem_calloc(sizeof(struct rect_set) + sizeof(struct rect) * R_GR);
239 	s->rl = R_GR;
240 	s->m = 0;
241 	return s;
242 }
243 
add_to_rect_set(struct rect_set ** s,struct rect * r)244 void add_to_rect_set(struct rect_set **s, struct rect *r)
245 {
246 	struct rect_set *ss = *s;
247 	int i;
248 	if (!is_rect_valid(r)) return;
249 	for (i = 0; i < ss->rl; i++) if (!ss->r[i].x1 && !ss->r[i].x2 && !ss->r[i].y1 && !ss->r[i].y2) {
250 		x:
251 		memcpy(&ss->r[i], r, sizeof(struct rect));
252 		if (i >= ss->m) ss->m = i + 1;
253 		return;
254 	}
255 	if ((unsigned)ss->rl > (MAXINT - sizeof(struct rect_set)) / sizeof(struct rect) - R_GR) overalloc();
256 	ss = mem_realloc(ss, sizeof(struct rect_set) + sizeof(struct rect) * (ss->rl + R_GR));
257 	memset(&(*s = ss)->r[i = (ss->rl += R_GR) - R_GR], 0, sizeof(struct rect) * R_GR);
258 	goto x;
259 }
260 
exclude_rect_from_set(struct rect_set ** s,struct rect * r)261 void exclude_rect_from_set(struct rect_set **s, struct rect *r)
262 {
263 	int i, a;
264 	struct rect *rr;
265 	do {
266 		a = 0;
267 		for (i = 0; i < (*s)->m; i++) if (do_rects_intersect(rr = &(*s)->r[i], r)) {
268 			struct rect r1, r2, r3, r4;
269 			r1.x1 = rr->x1;
270 			r1.x2 = rr->x2;
271 			r1.y1 = rr->y1;
272 			r1.y2 = r->y1;
273 
274 			r2.x1 = rr->x1;
275 			r2.x2 = r->x1;
276 			r2.y1 = r->y1;
277 			r2.y2 = r->y2;
278 
279 			r3.x1 = r->x2;
280 			r3.x2 = rr->x2;
281 			r3.y1 = r->y1;
282 			r3.y2 = r->y2;
283 
284 			r4.x1 = rr->x1;
285 			r4.x2 = rr->x2;
286 			r4.y1 = r->y2;
287 			r4.y2 = rr->y2;
288 
289 			intersect_rect(&r2, &r2, rr);
290 			intersect_rect(&r3, &r3, rr);
291 			rr->x1 = rr->x2 = rr->y1 = rr->y2 = 0;
292 #ifdef DEBUG
293 			if (is_rect_valid(&r1) && do_rects_intersect(&r1, r)) internal_error("bad intersection 1");
294 			if (is_rect_valid(&r2) && do_rects_intersect(&r2, r)) internal_error("bad intersection 2");
295 			if (is_rect_valid(&r3) && do_rects_intersect(&r3, r)) internal_error("bad intersection 3");
296 			if (is_rect_valid(&r4) && do_rects_intersect(&r4, r)) internal_error("bad intersection 4");
297 #endif
298 			add_to_rect_set(s, &r1);
299 			add_to_rect_set(s, &r2);
300 			add_to_rect_set(s, &r3);
301 			add_to_rect_set(s, &r4);
302 			a = 1;
303 		}
304 	} while (a);
305 }
306 
set_clip_area(struct graphics_device * dev,struct rect * r)307 void set_clip_area(struct graphics_device *dev, struct rect *r)
308 {
309 	dev->clip = *r;
310 	if (dev->clip.x1 < 0) dev->clip.x1 = 0;
311 	if (dev->clip.x2 > dev->size.x2) dev->clip.x2 = dev->size.x2;
312 	if (dev->clip.y1 < 0) dev->clip.y1 = 0;
313 	if (dev->clip.y2 > dev->size.y2) dev->clip.y2 = dev->size.y2;
314 	if (!is_rect_valid(&dev->clip)) {
315 		/* Empty region */
316 		dev->clip.x1 = dev->clip.x2 = dev->clip.y1 = dev->clip.y2 = 0;
317 	}
318 	if (drv->set_clip_area)
319 		drv->set_clip_area(dev);
320 }
321 
322 /* memory address r must contain one struct rect
323  * x1 is leftmost pixel that is still valid
324  * x2 is leftmost pixel that isn't valid any more
325  * y1, y2 analogically
326  */
restrict_clip_area(struct graphics_device * dev,struct rect * r,int x1,int y1,int x2,int y2)327 int restrict_clip_area(struct graphics_device *dev, struct rect *r, int x1, int y1, int x2, int y2)
328 {
329 	struct rect v, rr;
330 	rr.x1 = x1, rr.x2 = x2, rr.y1 = y1, rr.y2 = y2;
331 	if (r) memcpy(r, &dev->clip, sizeof(struct rect));
332 	intersect_rect(&v, &dev->clip, &rr);
333 	set_clip_area(dev, &v);
334 	return is_rect_valid(&v);
335 }
336 
g_scroll(struct graphics_device * dev,int scx,int scy)337 struct rect_set *g_scroll(struct graphics_device *dev, int scx, int scy)
338 {
339 	struct rect_set *rs = init_rect_set();
340 
341 	if (!scx && !scy)
342 		return rs;
343 	if (abs(scx) >= dev->clip.x2 - dev->clip.x1 ||
344 	    abs(scy) >= dev->clip.y2 - dev->clip.y1) {
345 		add_to_rect_set(&rs, &dev->clip);
346 		return rs;
347 	}
348 
349 	if (drv->scroll(dev, &rs, scx, scy)) {
350 		struct rect q = dev->clip;
351 		if (scy >= 0)
352 			q.y2 = q.y1 + scy;
353 		else
354 			q.y1 = q.y2 + scy;
355 		add_to_rect_set(&rs, &q);
356 
357 		q = dev->clip;
358 		if (scy >= 0)
359 			q.y1 += scy;
360 		else
361 			q.y2 += scy;
362 		if (scx >= 0)
363 			q.x2 = q.x1 + scx;
364 		else
365 			q.x1 = q.x2 + scx;
366 		add_to_rect_set(&rs, &q);
367 	}
368 
369 	return rs;
370 }
371 
372 #ifdef GRDRV_VIRTUAL_DEVICES
373 
374 struct graphics_device **virtual_devices;
375 int n_virtual_devices = 0;
376 struct graphics_device *current_virtual_device;
377 
378 static struct timer *virtual_device_timer;
379 
init_virtual_devices(struct graphics_driver * drv,int n)380 void init_virtual_devices(struct graphics_driver *drv, int n)
381 {
382 	if (n_virtual_devices) {
383 		internal_error("init_virtual_devices: already initialized");
384 		return;
385 	}
386 	if ((unsigned)n > MAXINT / sizeof(struct graphics_device *)) overalloc();
387 	virtual_devices = mem_calloc(n * sizeof(struct graphics_device *));
388 	n_virtual_devices = n;
389 	virtual_device_timer = NULL;
390 	current_virtual_device = NULL;
391 }
392 
init_virtual_device(void)393 struct graphics_device *init_virtual_device(void)
394 {
395 	int i;
396 	for (i = 0; i < n_virtual_devices; i++) if (!virtual_devices[i]) {
397 		struct graphics_device *dev;
398 		dev = mem_calloc(sizeof(struct graphics_device));
399 		dev->size.x2 = drv->x;
400 		dev->size.y2 = drv->y;
401 		current_virtual_device = virtual_devices[i] = dev;
402 		set_clip_area(dev, &dev->size);
403 		return dev;
404 	}
405 	return NULL;
406 }
407 
virtual_device_timer_fn(void * p)408 static void virtual_device_timer_fn(void *p)
409 {
410 	virtual_device_timer = NULL;
411 	if (current_virtual_device && current_virtual_device->redraw_handler) {
412 		set_clip_area(current_virtual_device, &current_virtual_device->size);
413 		current_virtual_device->redraw_handler(current_virtual_device, &current_virtual_device->size);
414 	}
415 }
416 
switch_virtual_device(int i)417 void switch_virtual_device(int i)
418 {
419 	if (i == VD_NEXT) {
420 		int j;
421 		int t = 0;
422 		for (j = 0; j < n_virtual_devices * 2; j++)
423 			if (virtual_devices[j % n_virtual_devices] == current_virtual_device) t = 1;
424 			else if (virtual_devices[j % n_virtual_devices] && t) {
425 				current_virtual_device = virtual_devices[j % n_virtual_devices];
426 				goto ok_switch;
427 			}
428 		return;
429 	}
430 	if (i < 0 || i >= n_virtual_devices || !virtual_devices[i]) return;
431 	current_virtual_device = virtual_devices[i];
432 	ok_switch:
433 	if (virtual_device_timer == NULL)
434 		virtual_device_timer = install_timer(0, virtual_device_timer_fn, NULL);
435 }
436 
shutdown_virtual_device(struct graphics_device * dev)437 void shutdown_virtual_device(struct graphics_device *dev)
438 {
439 	int i;
440 	for (i = 0; i < n_virtual_devices; i++) if (virtual_devices[i] == dev) {
441 		virtual_devices[i] = NULL;
442 		mem_free(dev);
443 		if (current_virtual_device != dev) return;
444 		for (; i < n_virtual_devices; i++) if (virtual_devices[i]) {
445 			switch_virtual_device(i);
446 			return;
447 		}
448 		for (i = 0; i < n_virtual_devices; i++) if (virtual_devices[i]) {
449 			switch_virtual_device(i);
450 			return;
451 		}
452 		current_virtual_device = NULL;
453 		return;
454 	}
455 	mem_free(dev);
456 	/*internal_error("shutdown_virtual_device: device not initialized");*/
457 }
458 
resize_virtual_devices(int x,int y)459 void resize_virtual_devices(int x, int y)
460 {
461 	int i;
462 	drv->x = x;
463 	drv->y = y;
464 	for (i = 0; i < n_virtual_devices; i++) {
465 		struct graphics_device *dev = virtual_devices[i];
466 		if (dev) {
467 			dev->size.x2 = x;
468 			dev->size.y2 = y;
469 			dev->resize_handler(dev);
470 		}
471 	}
472 }
473 
shutdown_virtual_devices(void)474 void shutdown_virtual_devices(void)
475 {
476 	int i;
477 	if (!n_virtual_devices) {
478 		internal_error("shutdown_virtual_devices: already shut down");
479 		return;
480 	}
481 	for (i = 0; i < n_virtual_devices; i++) if (virtual_devices[i]) internal_error("shutdown_virtual_devices: virtual device %d is still active", i);
482 	mem_free(virtual_devices);
483 	n_virtual_devices = 0;
484 	if (virtual_device_timer != NULL) kill_timer(virtual_device_timer), virtual_device_timer = NULL;
485 }
486 
487 #endif
488 
489 #if defined(GRDRV_X) || defined(GRDRV_HAIKU)
490 
491 /* This is executed in a helper thread, so we must not use mem_alloc */
492 
addchr(unsigned char ** str,size_t * l,unsigned char c)493 static void addchr(unsigned char **str, size_t *l, unsigned char c)
494 {
495 	unsigned char *s;
496 	if (!*str) return;
497 	if ((*str)[*l]) *l = strlen(cast_const_char *str);
498 	if (*l > MAXINT - 2) overalloc();
499 	s = realloc(*str, *l + 2);
500 	if (!s) {
501 		free(*str);
502 		*str = NULL;
503 		return;
504 	}
505 	*str = s;
506 	s[(*l)++] = c;
507 	s[*l] = 0;
508 }
509 
x_exec(unsigned char * command,int fg)510 int x_exec(unsigned char *command, int fg)
511 {
512 	unsigned char *pattern, *final;
513 	size_t i, j, l;
514 	int retval;
515 
516 	if (!fg) {
517 		retval = system(cast_const_char command);
518 		return retval;
519 	}
520 
521 	l = 0;
522 	if (*drv->param->shell_term) {
523 		pattern = cast_uchar strdup(cast_const_char drv->param->shell_term);
524 	} else {
525 		pattern = cast_uchar strdup(cast_const_char links_xterm());
526 		if (*command) {
527 			addchr(&pattern, &l, ' ');
528 			addchr(&pattern, &l, '%');
529 		}
530 	}
531 	if (!pattern) return -1;
532 
533 	final = cast_uchar strdup("");
534 	l = 0;
535 	for (i = 0; pattern[i]; i++) {
536 		if (pattern[i] == '%') {
537 			for (j = 0; j < strlen(cast_const_char command); j++)
538 				addchr(&final, &l, command[j]);
539 		} else {
540 			addchr(&final, &l, pattern[i]);
541 		}
542 	}
543 	free(pattern);
544 	if (!final) return -1;
545 
546 	retval = system(cast_const_char final);
547 	free(final);
548 	return retval;
549 }
550 
551 #endif
552 
553 #endif
554