560 lines
13 KiB
C
560 lines
13 KiB
C
/* drivers.c
|
|
* (c) 2002 Mikulas Patocka
|
|
* This file is a part of the Links program, released under GPL
|
|
*/
|
|
|
|
#include "cfg.h"
|
|
|
|
#ifdef G
|
|
|
|
#include "links.h"
|
|
|
|
int F = 0;
|
|
|
|
struct graphics_driver *drv = NULL;
|
|
|
|
#ifdef GRDRV_X
|
|
extern struct graphics_driver x_driver;
|
|
#endif
|
|
#ifdef GRDRV_SVGALIB
|
|
extern struct graphics_driver svga_driver;
|
|
#endif
|
|
#ifdef GRDRV_FB
|
|
extern struct graphics_driver fb_driver;
|
|
#endif
|
|
#ifdef GRDRV_DIRECTFB
|
|
extern struct graphics_driver directfb_driver;
|
|
#endif
|
|
#ifdef GRDRV_PMSHELL
|
|
extern struct graphics_driver pmshell_driver;
|
|
#endif
|
|
#ifdef GRDRV_ATHEOS
|
|
extern struct graphics_driver atheos_driver;
|
|
#endif
|
|
#ifdef GRDRV_HAIKU
|
|
extern struct graphics_driver haiku_driver;
|
|
#endif
|
|
#ifdef GRDRV_GRX
|
|
extern struct graphics_driver grx_driver;
|
|
#endif
|
|
#ifdef GRDRV_SDL
|
|
extern struct graphics_driver sdl_driver;
|
|
#endif
|
|
#ifdef GRDRV_SORTIX
|
|
extern struct graphics_driver sortix_driver;
|
|
#endif
|
|
|
|
|
|
/*
|
|
* On SPAD you must test first svgalib and then X (because X test is slow).
|
|
* On other systems you must test first X and then svgalib (because svgalib
|
|
* would work in X too and it's undesirable).
|
|
*/
|
|
|
|
static struct graphics_driver *graphics_drivers[] = {
|
|
#ifdef GRDRV_PMSHELL
|
|
&pmshell_driver,
|
|
#endif
|
|
#ifdef GRDRV_ATHEOS
|
|
&atheos_driver,
|
|
#endif
|
|
#ifdef GRDRV_HAIKU
|
|
&haiku_driver,
|
|
#endif
|
|
#ifndef SPAD
|
|
#ifdef GRDRV_X
|
|
&x_driver,
|
|
#endif
|
|
#endif
|
|
#ifdef GRDRV_FB
|
|
/* use FB before DirectFB because DirectFB has bugs */
|
|
&fb_driver,
|
|
#endif
|
|
#ifdef GRDRV_DIRECTFB
|
|
&directfb_driver,
|
|
#endif
|
|
#ifdef GRDRV_SVGALIB
|
|
&svga_driver,
|
|
#endif
|
|
#ifdef SPAD
|
|
#ifdef GRDRV_X
|
|
&x_driver,
|
|
#endif
|
|
#endif
|
|
#ifdef GRDRV_GRX
|
|
&grx_driver,
|
|
#endif
|
|
#ifdef GRDRV_SDL
|
|
&sdl_driver,
|
|
#endif
|
|
#ifdef GRDRV_SORTIX
|
|
&sortix_driver,
|
|
#endif
|
|
NULL
|
|
};
|
|
|
|
#if 0
|
|
static unsigned char *list_graphics_drivers(void)
|
|
{
|
|
unsigned char *d = init_str();
|
|
int l = 0;
|
|
struct graphics_driver **gd;
|
|
for (gd = graphics_drivers; *gd; gd++) {
|
|
if (l) add_chr_to_str(&d, &l, ' ');
|
|
add_to_str(&d, &l, (*gd)->name);
|
|
}
|
|
return d;
|
|
}
|
|
#endif
|
|
|
|
/* Driver je jednorazovy argument, kterej se preda grafickymu driveru, nikde se dal
|
|
* neuklada. Param se skladuje v default_driver param a uklada se do konfiguraku. Pred
|
|
* ukoncenim grafickeho driveru se nastavi default_driver_param podle
|
|
* drv->get_driver_param.
|
|
*/
|
|
static unsigned char *init_graphics_driver(struct graphics_driver *gd, unsigned char *param, unsigned char *display)
|
|
{
|
|
unsigned char *r;
|
|
unsigned char *p = param;
|
|
struct driver_param *dp = get_driver_param(gd->name);
|
|
if (!param || !*param) p = dp->param;
|
|
gd->param = dp;
|
|
drv = gd;
|
|
r = gd->init_driver(p,display);
|
|
if (r) drv = NULL;
|
|
else F = 1;
|
|
return r;
|
|
}
|
|
|
|
|
|
void add_graphics_drivers(unsigned char **s, int *l)
|
|
{
|
|
struct graphics_driver **gd;
|
|
for (gd = graphics_drivers; *gd; gd++) {
|
|
if (gd != graphics_drivers) add_to_str(s, l, cast_uchar ", ");
|
|
add_to_str(s, l, (*gd)->name);
|
|
}
|
|
}
|
|
|
|
unsigned char *init_graphics(unsigned char *driver, unsigned char *param, unsigned char *display)
|
|
{
|
|
unsigned char *s = init_str();
|
|
int l = 0;
|
|
struct graphics_driver **gd;
|
|
#if defined(GRDRV_PMSHELL) && defined(GRDRV_X)
|
|
if (is_xterm()) {
|
|
static unsigned char swapped = 0;
|
|
if (!swapped) {
|
|
for (gd = graphics_drivers; *gd; gd++) {
|
|
if (*gd == &pmshell_driver) *gd = &x_driver;
|
|
else if (*gd == &x_driver) *gd = &pmshell_driver;
|
|
}
|
|
swapped = 1;
|
|
}
|
|
}
|
|
#endif
|
|
for (gd = graphics_drivers; *gd; gd++) {
|
|
if (!driver || !*driver || !casestrcmp((*gd)->name, driver)) {
|
|
unsigned char *r;
|
|
if ((!driver || !*driver) && (*gd)->flags & GD_NOAUTO) continue;
|
|
if (!(r = init_graphics_driver(*gd, param, display))) {
|
|
mem_free(s);
|
|
return NULL;
|
|
}
|
|
if (!l) {
|
|
if (!driver || !*driver) add_to_str(&s, &l, cast_uchar "Could not initialize any graphics driver. Tried the following drivers:\n");
|
|
else add_to_str(&s, &l, cast_uchar "Could not initialize graphics driver ");
|
|
}
|
|
add_to_str(&s, &l, (*gd)->name);
|
|
add_to_str(&s, &l, cast_uchar ":\n");
|
|
add_to_str(&s, &l, r);
|
|
mem_free(r);
|
|
}
|
|
}
|
|
if (!l) {
|
|
add_to_str(&s, &l, cast_uchar "Unknown graphics driver ");
|
|
if (driver) add_to_str(&s, &l, driver);
|
|
add_to_str(&s, &l, cast_uchar ".\nThe following graphics drivers are supported:\n");
|
|
add_graphics_drivers(&s, &l);
|
|
add_to_str(&s, &l, cast_uchar "\n");
|
|
}
|
|
return s;
|
|
}
|
|
|
|
void shutdown_graphics(void)
|
|
{
|
|
if (drv) {
|
|
drv->shutdown_driver();
|
|
drv = NULL;
|
|
F = 0;
|
|
}
|
|
}
|
|
|
|
void update_driver_param(void)
|
|
{
|
|
if (drv) {
|
|
struct driver_param *dp = drv->param;
|
|
if (dp->param) mem_free(dp->param), dp->param = NULL;
|
|
if (drv->get_driver_param)
|
|
dp->param = stracpy(drv->get_driver_param());
|
|
dp->nosave = 0;
|
|
}
|
|
}
|
|
|
|
int g_kbd_codepage(struct graphics_driver *drv)
|
|
{
|
|
if (drv->param->kbd_codepage >= 0)
|
|
return drv->param->kbd_codepage;
|
|
return get_default_charset();
|
|
}
|
|
|
|
int do_rects_intersect(struct rect *r1, struct rect *r2)
|
|
{
|
|
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);
|
|
}
|
|
|
|
void intersect_rect(struct rect *v, struct rect *r1, struct rect *r2)
|
|
{
|
|
v->x1 = r1->x1 > r2->x1 ? r1->x1 : r2->x1;
|
|
v->x2 = r1->x2 > r2->x2 ? r2->x2 : r1->x2;
|
|
v->y1 = r1->y1 > r2->y1 ? r1->y1 : r2->y1;
|
|
v->y2 = r1->y2 > r2->y2 ? r2->y2 : r1->y2;
|
|
}
|
|
|
|
void unite_rect(struct rect *v, struct rect *r1, struct rect *r2)
|
|
{
|
|
if (!is_rect_valid(r1)) {
|
|
if (v != r2) memcpy(v, r2, sizeof(struct rect));
|
|
return;
|
|
}
|
|
if (!is_rect_valid(r2)) {
|
|
if (v != r1) memcpy(v, r1, sizeof(struct rect));
|
|
return;
|
|
}
|
|
v->x1 = r1->x1 < r2->x1 ? r1->x1 : r2->x1;
|
|
v->x2 = r1->x2 < r2->x2 ? r2->x2 : r1->x2;
|
|
v->y1 = r1->y1 < r2->y1 ? r1->y1 : r2->y1;
|
|
v->y2 = r1->y2 < r2->y2 ? r2->y2 : r1->y2;
|
|
}
|
|
|
|
#define R_GR 8
|
|
|
|
struct rect_set *init_rect_set(void)
|
|
{
|
|
struct rect_set *s;
|
|
s = mem_calloc(sizeof(struct rect_set) + sizeof(struct rect) * R_GR);
|
|
s->rl = R_GR;
|
|
s->m = 0;
|
|
return s;
|
|
}
|
|
|
|
void add_to_rect_set(struct rect_set **s, struct rect *r)
|
|
{
|
|
struct rect_set *ss = *s;
|
|
int i;
|
|
if (!is_rect_valid(r)) return;
|
|
for (i = 0; i < ss->rl; i++) if (!ss->r[i].x1 && !ss->r[i].x2 && !ss->r[i].y1 && !ss->r[i].y2) {
|
|
x:
|
|
memcpy(&ss->r[i], r, sizeof(struct rect));
|
|
if (i >= ss->m) ss->m = i + 1;
|
|
return;
|
|
}
|
|
if ((unsigned)ss->rl > (MAXINT - sizeof(struct rect_set)) / sizeof(struct rect) - R_GR) overalloc();
|
|
ss = mem_realloc(ss, sizeof(struct rect_set) + sizeof(struct rect) * (ss->rl + R_GR));
|
|
memset(&(*s = ss)->r[i = (ss->rl += R_GR) - R_GR], 0, sizeof(struct rect) * R_GR);
|
|
goto x;
|
|
}
|
|
|
|
void exclude_rect_from_set(struct rect_set **s, struct rect *r)
|
|
{
|
|
int i, a;
|
|
struct rect *rr;
|
|
do {
|
|
a = 0;
|
|
for (i = 0; i < (*s)->m; i++) if (do_rects_intersect(rr = &(*s)->r[i], r)) {
|
|
struct rect r1, r2, r3, r4;
|
|
r1.x1 = rr->x1;
|
|
r1.x2 = rr->x2;
|
|
r1.y1 = rr->y1;
|
|
r1.y2 = r->y1;
|
|
|
|
r2.x1 = rr->x1;
|
|
r2.x2 = r->x1;
|
|
r2.y1 = r->y1;
|
|
r2.y2 = r->y2;
|
|
|
|
r3.x1 = r->x2;
|
|
r3.x2 = rr->x2;
|
|
r3.y1 = r->y1;
|
|
r3.y2 = r->y2;
|
|
|
|
r4.x1 = rr->x1;
|
|
r4.x2 = rr->x2;
|
|
r4.y1 = r->y2;
|
|
r4.y2 = rr->y2;
|
|
|
|
intersect_rect(&r2, &r2, rr);
|
|
intersect_rect(&r3, &r3, rr);
|
|
rr->x1 = rr->x2 = rr->y1 = rr->y2 = 0;
|
|
#ifdef DEBUG
|
|
if (is_rect_valid(&r1) && do_rects_intersect(&r1, r)) internal_error("bad intersection 1");
|
|
if (is_rect_valid(&r2) && do_rects_intersect(&r2, r)) internal_error("bad intersection 2");
|
|
if (is_rect_valid(&r3) && do_rects_intersect(&r3, r)) internal_error("bad intersection 3");
|
|
if (is_rect_valid(&r4) && do_rects_intersect(&r4, r)) internal_error("bad intersection 4");
|
|
#endif
|
|
add_to_rect_set(s, &r1);
|
|
add_to_rect_set(s, &r2);
|
|
add_to_rect_set(s, &r3);
|
|
add_to_rect_set(s, &r4);
|
|
a = 1;
|
|
}
|
|
} while (a);
|
|
}
|
|
|
|
void set_clip_area(struct graphics_device *dev, struct rect *r)
|
|
{
|
|
dev->clip = *r;
|
|
if (dev->clip.x1 < 0) dev->clip.x1 = 0;
|
|
if (dev->clip.x2 > dev->size.x2) dev->clip.x2 = dev->size.x2;
|
|
if (dev->clip.y1 < 0) dev->clip.y1 = 0;
|
|
if (dev->clip.y2 > dev->size.y2) dev->clip.y2 = dev->size.y2;
|
|
if (!is_rect_valid(&dev->clip)) {
|
|
/* Empty region */
|
|
dev->clip.x1 = dev->clip.x2 = dev->clip.y1 = dev->clip.y2 = 0;
|
|
}
|
|
if (drv->set_clip_area)
|
|
drv->set_clip_area(dev);
|
|
}
|
|
|
|
/* memory address r must contain one struct rect
|
|
* x1 is leftmost pixel that is still valid
|
|
* x2 is leftmost pixel that isn't valid any more
|
|
* y1, y2 analogically
|
|
*/
|
|
int restrict_clip_area(struct graphics_device *dev, struct rect *r, int x1, int y1, int x2, int y2)
|
|
{
|
|
struct rect v, rr;
|
|
rr.x1 = x1, rr.x2 = x2, rr.y1 = y1, rr.y2 = y2;
|
|
if (r) memcpy(r, &dev->clip, sizeof(struct rect));
|
|
intersect_rect(&v, &dev->clip, &rr);
|
|
set_clip_area(dev, &v);
|
|
return is_rect_valid(&v);
|
|
}
|
|
|
|
struct rect_set *g_scroll(struct graphics_device *dev, int scx, int scy)
|
|
{
|
|
struct rect_set *rs = init_rect_set();
|
|
|
|
if (!scx && !scy)
|
|
return rs;
|
|
if (abs(scx) >= dev->clip.x2 - dev->clip.x1 ||
|
|
abs(scy) >= dev->clip.y2 - dev->clip.y1) {
|
|
add_to_rect_set(&rs, &dev->clip);
|
|
return rs;
|
|
}
|
|
|
|
if (drv->scroll(dev, &rs, scx, scy)) {
|
|
struct rect q = dev->clip;
|
|
if (scy >= 0)
|
|
q.y2 = q.y1 + scy;
|
|
else
|
|
q.y1 = q.y2 + scy;
|
|
add_to_rect_set(&rs, &q);
|
|
|
|
q = dev->clip;
|
|
if (scy >= 0)
|
|
q.y1 += scy;
|
|
else
|
|
q.y2 += scy;
|
|
if (scx >= 0)
|
|
q.x2 = q.x1 + scx;
|
|
else
|
|
q.x1 = q.x2 + scx;
|
|
add_to_rect_set(&rs, &q);
|
|
}
|
|
|
|
return rs;
|
|
}
|
|
|
|
#ifdef GRDRV_VIRTUAL_DEVICES
|
|
|
|
struct graphics_device **virtual_devices;
|
|
int n_virtual_devices = 0;
|
|
struct graphics_device *current_virtual_device;
|
|
|
|
static struct timer *virtual_device_timer;
|
|
|
|
void init_virtual_devices(struct graphics_driver *drv, int n)
|
|
{
|
|
if (n_virtual_devices) {
|
|
internal_error("init_virtual_devices: already initialized");
|
|
return;
|
|
}
|
|
if ((unsigned)n > MAXINT / sizeof(struct graphics_device *)) overalloc();
|
|
virtual_devices = mem_calloc(n * sizeof(struct graphics_device *));
|
|
n_virtual_devices = n;
|
|
virtual_device_timer = NULL;
|
|
current_virtual_device = NULL;
|
|
}
|
|
|
|
struct graphics_device *init_virtual_device(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < n_virtual_devices; i++) if (!virtual_devices[i]) {
|
|
struct graphics_device *dev;
|
|
dev = mem_calloc(sizeof(struct graphics_device));
|
|
dev->size.x2 = drv->x;
|
|
dev->size.y2 = drv->y;
|
|
current_virtual_device = virtual_devices[i] = dev;
|
|
set_clip_area(dev, &dev->size);
|
|
return dev;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void virtual_device_timer_fn(void *p)
|
|
{
|
|
virtual_device_timer = NULL;
|
|
if (current_virtual_device && current_virtual_device->redraw_handler) {
|
|
set_clip_area(current_virtual_device, ¤t_virtual_device->size);
|
|
current_virtual_device->redraw_handler(current_virtual_device, ¤t_virtual_device->size);
|
|
}
|
|
}
|
|
|
|
void switch_virtual_device(int i)
|
|
{
|
|
if (i == VD_NEXT) {
|
|
int j;
|
|
int t = 0;
|
|
for (j = 0; j < n_virtual_devices * 2; j++)
|
|
if (virtual_devices[j % n_virtual_devices] == current_virtual_device) t = 1;
|
|
else if (virtual_devices[j % n_virtual_devices] && t) {
|
|
current_virtual_device = virtual_devices[j % n_virtual_devices];
|
|
goto ok_switch;
|
|
}
|
|
return;
|
|
}
|
|
if (i < 0 || i >= n_virtual_devices || !virtual_devices[i]) return;
|
|
current_virtual_device = virtual_devices[i];
|
|
ok_switch:
|
|
if (virtual_device_timer == NULL)
|
|
virtual_device_timer = install_timer(0, virtual_device_timer_fn, NULL);
|
|
}
|
|
|
|
void shutdown_virtual_device(struct graphics_device *dev)
|
|
{
|
|
int i;
|
|
for (i = 0; i < n_virtual_devices; i++) if (virtual_devices[i] == dev) {
|
|
virtual_devices[i] = NULL;
|
|
mem_free(dev);
|
|
if (current_virtual_device != dev) return;
|
|
for (; i < n_virtual_devices; i++) if (virtual_devices[i]) {
|
|
switch_virtual_device(i);
|
|
return;
|
|
}
|
|
for (i = 0; i < n_virtual_devices; i++) if (virtual_devices[i]) {
|
|
switch_virtual_device(i);
|
|
return;
|
|
}
|
|
current_virtual_device = NULL;
|
|
return;
|
|
}
|
|
mem_free(dev);
|
|
/*internal_error("shutdown_virtual_device: device not initialized");*/
|
|
}
|
|
|
|
void resize_virtual_devices(int x, int y)
|
|
{
|
|
int i;
|
|
drv->x = x;
|
|
drv->y = y;
|
|
for (i = 0; i < n_virtual_devices; i++) {
|
|
struct graphics_device *dev = virtual_devices[i];
|
|
if (dev) {
|
|
dev->size.x2 = x;
|
|
dev->size.y2 = y;
|
|
dev->resize_handler(dev);
|
|
}
|
|
}
|
|
}
|
|
|
|
void shutdown_virtual_devices(void)
|
|
{
|
|
int i;
|
|
if (!n_virtual_devices) {
|
|
internal_error("shutdown_virtual_devices: already shut down");
|
|
return;
|
|
}
|
|
for (i = 0; i < n_virtual_devices; i++) if (virtual_devices[i]) internal_error("shutdown_virtual_devices: virtual device %d is still active", i);
|
|
mem_free(virtual_devices);
|
|
n_virtual_devices = 0;
|
|
if (virtual_device_timer != NULL) kill_timer(virtual_device_timer), virtual_device_timer = NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(GRDRV_X) || defined(GRDRV_HAIKU)
|
|
|
|
/* This is executed in a helper thread, so we must not use mem_alloc */
|
|
|
|
static void addchr(unsigned char **str, size_t *l, unsigned char c)
|
|
{
|
|
unsigned char *s;
|
|
if (!*str) return;
|
|
if ((*str)[*l]) *l = strlen(cast_const_char *str);
|
|
if (*l > MAXINT - 2) overalloc();
|
|
s = realloc(*str, *l + 2);
|
|
if (!s) {
|
|
free(*str);
|
|
*str = NULL;
|
|
return;
|
|
}
|
|
*str = s;
|
|
s[(*l)++] = c;
|
|
s[*l] = 0;
|
|
}
|
|
|
|
int x_exec(unsigned char *command, int fg)
|
|
{
|
|
unsigned char *pattern, *final;
|
|
size_t i, j, l;
|
|
int retval;
|
|
|
|
if (!fg) {
|
|
retval = system(cast_const_char command);
|
|
return retval;
|
|
}
|
|
|
|
l = 0;
|
|
if (*drv->param->shell_term) {
|
|
pattern = cast_uchar strdup(cast_const_char drv->param->shell_term);
|
|
} else {
|
|
pattern = cast_uchar strdup(cast_const_char links_xterm());
|
|
if (*command) {
|
|
addchr(&pattern, &l, ' ');
|
|
addchr(&pattern, &l, '%');
|
|
}
|
|
}
|
|
if (!pattern) return -1;
|
|
|
|
final = cast_uchar strdup("");
|
|
l = 0;
|
|
for (i = 0; pattern[i]; i++) {
|
|
if (pattern[i] == '%') {
|
|
for (j = 0; j < strlen(cast_const_char command); j++)
|
|
addchr(&final, &l, command[j]);
|
|
} else {
|
|
addchr(&final, &l, pattern[i]);
|
|
}
|
|
}
|
|
free(pattern);
|
|
if (!final) return -1;
|
|
|
|
retval = system(cast_const_char final);
|
|
free(final);
|
|
return retval;
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|