577 lines
15 KiB
C
577 lines
15 KiB
C
#include "cfg.h"
|
|
|
|
#ifdef GRDRV_GRX
|
|
|
|
#include "links.h"
|
|
|
|
#include <grx20.h>
|
|
|
|
#include "arrow.inc"
|
|
|
|
#define NUMBER_OF_DEVICES 12
|
|
|
|
static void grx_mouse_init(void);
|
|
static void grx_mouse_terminate(void);
|
|
|
|
void _GrCloseVideoDriver(void);
|
|
|
|
extern struct graphics_driver grx_driver;
|
|
|
|
static struct itrm *svgalib_kbd;
|
|
|
|
static unsigned char *grx_driver_param = NULL;
|
|
static struct graphics_device *grx_old_vd;
|
|
static unsigned char in_graphics_mode = 0;
|
|
|
|
#define TEST_INACTIVITY if (dev != current_virtual_device || !dev->clip.x2) return;
|
|
#define TEST_INACTIVITY_0 if (dev != current_virtual_device || !dev->clip.x2) return 0;
|
|
|
|
static void grx_set_palette(void)
|
|
{
|
|
unsigned i, colors;
|
|
if (grx_driver.depth == 33)
|
|
colors = 16;
|
|
else if (grx_driver.depth == (65 | 0x300))
|
|
colors = 216;
|
|
else if (grx_driver.depth == 65)
|
|
colors = 256;
|
|
else
|
|
return;
|
|
GrResetColors();
|
|
for (i = 0; i < colors; i++) {
|
|
unsigned rgb[3];
|
|
q_palette(colors, i, 255, rgb);
|
|
GrSetColor(i, rgb[0], rgb[1], rgb[2]);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void grx_update_palette(void)
|
|
{
|
|
int new_depth;
|
|
if (!grx_driver.param->palette_mode)
|
|
new_depth = grx_driver.depth | 0x300;
|
|
else
|
|
new_depth = grx_driver.depth & ~0x300;
|
|
|
|
if (new_depth == grx_driver.depth)
|
|
return;
|
|
|
|
grx_driver.depth = new_depth;
|
|
|
|
grx_driver.get_color = get_color_fn(new_depth);
|
|
if (!grx_driver.get_color) internal_error("Unknown bit depth %x", new_depth);
|
|
|
|
grx_mouse_terminate();
|
|
grx_set_palette();
|
|
grx_mouse_init();
|
|
}
|
|
|
|
static void grx_set_text(void)
|
|
{
|
|
if (in_graphics_mode) {
|
|
GrSetMode(GR_default_text);
|
|
in_graphics_mode = 0;
|
|
_GrCloseVideoDriver();
|
|
restore_terminal();
|
|
}
|
|
}
|
|
|
|
static void grx_restore(void)
|
|
{
|
|
if (grx_driver_param) mem_free(grx_driver_param), grx_driver_param = NULL;
|
|
grx_set_text();
|
|
}
|
|
|
|
static void grx_set_clip(void)
|
|
{
|
|
GrSetClipBox(
|
|
current_virtual_device->clip.x1,
|
|
current_virtual_device->clip.y1,
|
|
current_virtual_device->clip.x2 - 1,
|
|
current_virtual_device->clip.y2 - 1
|
|
);
|
|
}
|
|
|
|
static void grx_key_in(struct itrm *p, unsigned char *ev_, int size)
|
|
{
|
|
int vd;
|
|
struct links_event *ev = (struct links_event *)(void *)ev_;
|
|
if (size != sizeof(struct links_event)) return;
|
|
if (ev->ev == EV_ABORT) terminate_loop = 1;
|
|
if (ev->ev != EV_KBD) return;
|
|
if (ev->y & KBD_PASTING) goto skip;
|
|
if (((ev->y & (KBD_CTRL | KBD_ALT)) == KBD_ALT) && ev->x >= '0' && ev->x <= '9') {
|
|
vd = (ev->x - '1' + 10) % 10;
|
|
goto switch_vd;
|
|
}
|
|
if (((ev->y & (KBD_CTRL | KBD_ALT)) == KBD_ALT) && ev->x == '-') {
|
|
vd = 10;
|
|
goto switch_vd;
|
|
}
|
|
if (((ev->y & (KBD_CTRL | KBD_ALT)) == KBD_ALT) && ev->x == '=') {
|
|
vd = 11;
|
|
goto switch_vd;
|
|
}
|
|
if (((ev->y & (KBD_CTRL | KBD_ALT)) == KBD_ALT) && ev->x <= KBD_F1 && ev->x >= KBD_F12) {
|
|
vd = KBD_F1 - ev->x;
|
|
goto switch_vd;
|
|
}
|
|
skip:
|
|
if (g_kbd_codepage(&grx_driver) != utf8_table && ev->x >= 128 && ev->x <= 255)
|
|
if ((ev->x = cp2u(ev->x, g_kbd_codepage(&grx_driver))) == -1) return;
|
|
if (current_virtual_device && current_virtual_device->keyboard_handler) current_virtual_device->keyboard_handler(current_virtual_device, ev->x, ev->y);
|
|
return;
|
|
|
|
switch_vd:
|
|
switch_virtual_device(vd);
|
|
grx_set_clip();
|
|
}
|
|
|
|
int grx_mouse_initialized = 0;
|
|
static GrCursor *grx_mouse_cursor;
|
|
|
|
static void grx_mouse_init(void)
|
|
{
|
|
int x, y;
|
|
static unsigned char cursor[arrow_area];
|
|
static GrColor table[3];
|
|
|
|
if (!GrMouseDetect()) return;
|
|
grx_mouse_initialized = 1;
|
|
GrMouseInit();
|
|
GrMouseEventEnable(0, 1);
|
|
|
|
memset(&table, 0, sizeof table);
|
|
table[0] = 2;
|
|
table[1] = grx_driver.get_color(0);
|
|
table[2] = grx_driver.get_color(0xffffff);
|
|
for (y = 0; y < arrow_height; y++)
|
|
for (x = 0; x < arrow_width; x++)
|
|
cursor[y * arrow_width + x] =
|
|
(arrow[2 * y] >> (arrow_width - x - 1)) & 1 ? 1 :
|
|
(arrow[2 * y + 1] >> (arrow_width - x - 1)) & 1 ? 2 :
|
|
0;
|
|
|
|
grx_mouse_cursor = GrBuildCursor(cast_char cursor, arrow_width, arrow_width, arrow_height, 0, 0, table);
|
|
|
|
if (!grx_mouse_cursor) GrMouseSetColors(grx_driver.get_color(0xffffff), grx_driver.get_color(0x000000));
|
|
else GrMouseSetCursor(grx_mouse_cursor);
|
|
|
|
GrMouseDisplayCursor();
|
|
}
|
|
|
|
static void grx_mouse_terminate(void)
|
|
{
|
|
if (!grx_mouse_initialized) return;
|
|
GrMouseEraseCursor();
|
|
GrMouseUnInit();
|
|
if (grx_mouse_cursor) GrDestroyCursor(grx_mouse_cursor);
|
|
grx_mouse_initialized = 0;
|
|
}
|
|
|
|
void grx_mouse_poll(void)
|
|
{
|
|
int x = GrMousePendingEvent();
|
|
x = x;
|
|
}
|
|
|
|
static void grx_do_event(GrMouseEvent *ev, int b)
|
|
{
|
|
if (!current_virtual_device) return;
|
|
if (current_virtual_device->mouse_handler) current_virtual_device->mouse_handler(current_virtual_device, ev->x, ev->y, b);
|
|
}
|
|
|
|
int grx_mouse_event(void)
|
|
{
|
|
int e;
|
|
GrMouseEvent ev;
|
|
e = 0;
|
|
get_another:
|
|
GrMouseGetEvent(GR_M_MOTION | GR_M_BUTTON_CHANGE | GR_M_POLL, &ev);
|
|
if (ev.flags & GR_M_LEFT_DOWN) grx_do_event(&ev, B_LEFT | B_DOWN), e = 1;
|
|
if (ev.flags & GR_M_LEFT_UP) grx_do_event(&ev, B_LEFT | B_UP), e = 1;
|
|
if (ev.flags & GR_M_RIGHT_DOWN) grx_do_event(&ev, B_RIGHT | B_DOWN), e = 1;
|
|
if (ev.flags & GR_M_RIGHT_UP) grx_do_event(&ev, B_RIGHT | B_UP), e = 1;
|
|
if (ev.flags & GR_M_MIDDLE_DOWN) grx_do_event(&ev, B_MIDDLE | B_DOWN), e = 1;
|
|
if (ev.flags & GR_M_MIDDLE_UP) grx_do_event(&ev, B_MIDDLE | B_UP), e = 1;
|
|
if (ev.flags & GR_M_P4_DOWN) grx_do_event(&ev, B_WHEELUP | B_MOVE), e = 1;
|
|
if (ev.flags & GR_M_P5_DOWN) grx_do_event(&ev, B_WHEELDOWN | B_MOVE), e = 1;
|
|
if (!e && ev.flags & GR_M_MOTION) {
|
|
if (GrMousePendingEvent())
|
|
goto get_another;
|
|
grx_do_event(&ev,
|
|
ev.buttons & GR_M_LEFT ? B_LEFT | B_DRAG :
|
|
ev.buttons & GR_M_RIGHT ? B_RIGHT | B_DRAG :
|
|
ev.buttons & GR_M_MIDDLE ? B_DRAG | B_MIDDLE : B_MOVE), e = 1;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
static unsigned char *set_mode_according_to_param(unsigned char *param)
|
|
{
|
|
unsigned char e[MAX_STR_LEN];
|
|
save_terminal();
|
|
if (!param || !*param) {
|
|
return stracpy(cast_uchar "There is no default videomode.\nUse the switch \"-mode WIDTHxHEIGHTxCOLORS\" to select video mode.\nThe COLORS argument may be 16, 256, 32k, 64k, 16M.\n");
|
|
#if 0
|
|
if (!GrSetMode(GR_default_graphics))
|
|
return stracpy(cast_uchar "GrSetMode failed.\n");
|
|
in_graphics_mode = 1;
|
|
#endif
|
|
} else {
|
|
unsigned long x, y;
|
|
int pl;
|
|
unsigned char *ptr = param;
|
|
char *end;
|
|
x = strtoul(cast_const_char ptr, &end, 10);
|
|
if (cast_uchar end == ptr || *end != 'x' || x >= MAXINT)
|
|
goto bad_param;
|
|
ptr = cast_uchar end + 1;
|
|
y = strtoul(cast_const_char ptr, &end, 10);
|
|
if (cast_uchar end == ptr || *end != 'x' || y >= MAXINT)
|
|
goto bad_param;
|
|
ptr = cast_uchar end + 1;
|
|
if (!strcmp(cast_const_char ptr, "16")) pl = 4;
|
|
else if (!strcmp(cast_const_char ptr, "256")) pl = 8;
|
|
else if (!casestrcmp(ptr, cast_uchar "32k") || !strcmp(cast_const_char ptr, "32768")) pl = 15;
|
|
else if (!casestrcmp(ptr, cast_uchar "64k") || !strcmp(cast_const_char ptr, "65536")) pl = 16;
|
|
else if (!casestrcmp(ptr, cast_uchar "16M") || !casestrcmp(ptr, cast_uchar "16M32") || !strcmp(cast_const_char ptr, "16777216")) pl = 24;
|
|
else goto bad_param;
|
|
if (x == 800 && y == 600 && pl == 4) y = 599; /* a bug in GRX */
|
|
if (!GrSetMode(GR_width_height_bpp_graphics, (int)x, (int)y, pl)) {
|
|
snprintf(cast_char e, MAX_STR_LEN, "GrSetMode(%lu,%lu,%d) failed.\n", x, y, pl);
|
|
return stracpy(e);
|
|
}
|
|
in_graphics_mode = 1;
|
|
}
|
|
return NULL;
|
|
|
|
bad_param:
|
|
return stracpy(cast_uchar "Bad -mode parameter.\n");
|
|
}
|
|
|
|
static void grx_set_graphics(void)
|
|
{
|
|
if (!in_graphics_mode) {
|
|
unsigned char *er;
|
|
er = set_mode_according_to_param(grx_driver_param);
|
|
if (er)
|
|
fatal_exit("grx: Unable to restore video mode '%s': %s", grx_driver_param, er);
|
|
}
|
|
}
|
|
|
|
static unsigned char *grx_init_driver(unsigned char *param, unsigned char *ignore)
|
|
{
|
|
unsigned char e[MAX_STR_LEN];
|
|
unsigned char *er;
|
|
const GrVideoMode *mode;
|
|
const GrFrameDriver *fd;
|
|
|
|
kbd_set_raw = 1;
|
|
|
|
grx_old_vd = NULL;
|
|
|
|
er = set_mode_according_to_param(param);
|
|
if (er)
|
|
return er;
|
|
|
|
mode = GrCurrentVideoMode();
|
|
fd = GrScreenFrameDriver();
|
|
if (GrNumPlanes() != (mode->bpp == 4 ? 4 : 1)) {
|
|
unsupported_videomode:
|
|
snprintf(cast_char e, MAX_STR_LEN, "Unsupported videomode: x %d, y %d, bpp (%d, %d), planes %d.\n", mode->width, mode->height, mode->bpp, fd->bits_per_pixel, (int)GrNumPlanes());
|
|
grx_restore();
|
|
return stracpy(e);
|
|
}
|
|
grx_driver.flags &= ~GD_SELECT_PALETTE;
|
|
switch (mode->bpp) {
|
|
case 4:
|
|
grx_driver.depth = 33;
|
|
break;
|
|
case 8:
|
|
grx_driver.depth = 65;
|
|
if (!grx_driver.param->palette_mode)
|
|
grx_driver.depth |= 0x300;
|
|
grx_driver.flags |= GD_SELECT_PALETTE;
|
|
break;
|
|
case 15:
|
|
grx_driver.depth = 122;
|
|
break;
|
|
case 16:
|
|
grx_driver.depth = 130;
|
|
break;
|
|
case 24:
|
|
case 32:
|
|
if (fd->bits_per_pixel == 24)
|
|
grx_driver.depth = 195;
|
|
else if (fd->bits_per_pixel == 32)
|
|
grx_driver.depth = 196;
|
|
else
|
|
goto unsupported_videomode;
|
|
break;
|
|
default:
|
|
goto unsupported_videomode;
|
|
}
|
|
grx_driver.get_color = get_color_fn(grx_driver.depth);
|
|
if (!grx_driver.get_color) internal_error("Unknown bit depth %x", grx_driver.depth);
|
|
grx_driver.x = mode->width;
|
|
grx_driver.y = mode->height;
|
|
if (mode->bpp >= 8) grx_driver.flags |= GD_DONT_USE_SCROLL;
|
|
else grx_driver.flags &= ~GD_DONT_USE_SCROLL;
|
|
/*fatal_exit("current mode: %d,%d,%d planes %d bits %d %d", mode->width, mode->height, mode->bpp, GrNumPlanes(), GrScreenFrameDriver()->bits_per_pixel, mode->bpp);*/
|
|
|
|
grx_set_palette();
|
|
|
|
grx_driver_param = stracpy(param ? param : cast_uchar "");
|
|
|
|
init_virtual_devices(&grx_driver, NUMBER_OF_DEVICES);
|
|
|
|
svgalib_kbd = handle_svgalib_keyboard(grx_key_in);
|
|
grx_mouse_init();
|
|
return NULL;
|
|
}
|
|
|
|
static void grx_shutdown_driver(void)
|
|
{
|
|
shutdown_virtual_devices();
|
|
grx_mouse_terminate();
|
|
svgalib_free_trm(svgalib_kbd);
|
|
grx_restore();
|
|
}
|
|
|
|
static void grx_emergency_shutdown(void)
|
|
{
|
|
grx_restore();
|
|
}
|
|
|
|
static unsigned char *grx_get_driver_param(void)
|
|
{
|
|
return grx_driver_param;
|
|
}
|
|
|
|
struct grx_bmp {
|
|
GrContext c;
|
|
};
|
|
|
|
static int grx_get_empty_bitmap(struct bitmap *bmp)
|
|
{
|
|
struct grx_bmp *b;
|
|
|
|
if (!in_graphics_mode)
|
|
internal_error("grx_get_empty_bitmap: not in graphics mode");
|
|
|
|
b = mem_alloc_mayfail(sizeof(struct grx_bmp));
|
|
if (!b) goto fail0;
|
|
again1:
|
|
if (!GrCreateContext(bmp->x, bmp->y, NULL, &b->c)) {
|
|
if (out_of_memory(0, NULL, 0))
|
|
goto again1;
|
|
goto fail1;
|
|
}
|
|
bmp->flags = b;
|
|
if (GrNumPlanes() == 1) {
|
|
bmp->data = b->c.gc_baseaddr[0];
|
|
bmp->skip = b->c.gc_lineoffset;
|
|
} else {
|
|
if (bmp->x && (unsigned)bmp->x * (unsigned)bmp->y / (unsigned)bmp->x != (unsigned)bmp->y) goto fail2;
|
|
if ((unsigned)bmp->x * (unsigned)bmp->y > (unsigned)MAXINT) goto fail2;
|
|
bmp->skip = bmp->x;
|
|
bmp->data = mem_alloc_mayfail(bmp->skip * bmp->y);
|
|
if (!bmp->data) goto fail2;
|
|
}
|
|
return 0;
|
|
|
|
fail2:
|
|
GrDestroyContext(&b->c);
|
|
fail1:
|
|
mem_free(b);
|
|
fail0:
|
|
bmp->data = NULL;
|
|
bmp->flags = NULL;
|
|
return -1;
|
|
}
|
|
|
|
static void grx_copy_planes(struct bitmap *bmp, int y, int n)
|
|
{
|
|
struct grx_bmp *b = bmp->flags;
|
|
int j;
|
|
if (!b) return;
|
|
for (j = 0; j < n; y++, j++) {
|
|
int i;
|
|
for (i = 0; i < GrNumPlanes(); i++) {
|
|
int x;
|
|
unsigned char *src = (unsigned char *)bmp->data + bmp->skip * j;
|
|
unsigned char *dst = (unsigned char *)b->c.gc_baseaddr[i] + y * b->c.gc_lineoffset;
|
|
unsigned char byt = 0;
|
|
for (x = 0; x < bmp->x; ) {
|
|
unsigned char bit = (*src++ >> i) & 1;
|
|
byt |= bit << ((x & 7) ^ 7);
|
|
x++;
|
|
if (!(x & 7)) *dst++ = byt, byt = 0;
|
|
}
|
|
if (x & 7) *dst = byt;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void grx_register_bitmap(struct bitmap *bmp)
|
|
{
|
|
if (GrNumPlanes() != 1) {
|
|
grx_copy_planes(bmp, 0, bmp->y);
|
|
mem_free(bmp->data);
|
|
}
|
|
}
|
|
|
|
static void *grx_prepare_strip(struct bitmap *bmp, int top, int lines)
|
|
{
|
|
struct grx_bmp *b = bmp->flags;
|
|
if (!b) return NULL;
|
|
if (GrNumPlanes() != 1) {
|
|
bmp->data = mem_alloc_mayfail(bmp->skip * lines);
|
|
return bmp->data;
|
|
}
|
|
return ((unsigned char *)bmp->data) + bmp->skip * top;
|
|
}
|
|
|
|
|
|
static void grx_commit_strip(struct bitmap *bmp, int top, int lines)
|
|
{
|
|
if (GrNumPlanes() != 1) {
|
|
if (!bmp->data) return;
|
|
grx_copy_planes(bmp, top, lines);
|
|
mem_free(bmp->data);
|
|
}
|
|
}
|
|
|
|
static void grx_unregister_bitmap(struct bitmap *bmp)
|
|
{
|
|
struct grx_bmp *b = bmp->flags;
|
|
if (!b) return;
|
|
GrDestroyContext(&b->c);
|
|
mem_free(b);
|
|
}
|
|
|
|
static void grx_draw_bitmap(struct graphics_device *dev, struct bitmap *bmp, int x, int y)
|
|
{
|
|
struct grx_bmp *b = bmp->flags;
|
|
if (!b) return;
|
|
TEST_INACTIVITY
|
|
CLIP_DRAW_BITMAP
|
|
/* note: GrImageDisplay has buggy clipping.
|
|
It doesn't draw if the target x or y coordinate is one pixel
|
|
before the end of clip area */
|
|
GrBitBlt(NULL, x, y, &b->c, 0, 0, bmp->x - 1, bmp->y - 1, GrWRITE);
|
|
}
|
|
|
|
static void grx_fill_area(struct graphics_device *dev, int x1, int y1, int x2, int y2, long color)
|
|
{
|
|
TEST_INACTIVITY
|
|
CLIP_FILL_AREA
|
|
if (x1 >= x2 || y1 >= y2) return;
|
|
GrFilledBox(x1, y1, x2 - 1, y2 - 1, color);
|
|
}
|
|
|
|
static void grx_draw_hline(struct graphics_device *dev, int x1, int y, int x2, long color)
|
|
{
|
|
TEST_INACTIVITY
|
|
CLIP_DRAW_HLINE
|
|
GrHLine(x1, x2 - 1, y, color);
|
|
}
|
|
|
|
static void grx_draw_vline(struct graphics_device *dev, int x, int y1, int y2, long color)
|
|
{
|
|
TEST_INACTIVITY
|
|
CLIP_DRAW_VLINE
|
|
GrVLine(x, y1, y2 - 1, color);
|
|
}
|
|
|
|
static int grx_scroll(struct graphics_device *dev, struct rect_set **ignore, int scx, int scy)
|
|
{
|
|
TEST_INACTIVITY_0
|
|
GrBitBlt(NULL,
|
|
dev->clip.x1 + (scx >= 0 ? scx : 0),
|
|
dev->clip.y1 + (scy >= 0 ? scy : 0),
|
|
NULL,
|
|
dev->clip.x1 - (scx < 0 ? scx : 0),
|
|
dev->clip.y1 - (scy < 0 ? scy : 0),
|
|
dev->clip.x2 - (scx >= 0 ? scx : 0) - 1,
|
|
dev->clip.y2 - (scy >= 0 ? scy : 0) - 1,
|
|
GrWRITE);
|
|
return 1;
|
|
}
|
|
|
|
static void grx_set_clip_area(struct graphics_device *dev)
|
|
{
|
|
if (dev != current_virtual_device) return;
|
|
grx_set_clip();
|
|
}
|
|
|
|
static int grx_block(struct graphics_device *dev)
|
|
{
|
|
if (grx_old_vd) return 1;
|
|
grx_old_vd = current_virtual_device;
|
|
current_virtual_device = NULL;
|
|
grx_mouse_terminate();
|
|
svgalib_block_itrm(svgalib_kbd);
|
|
grx_set_text();
|
|
return 0;
|
|
}
|
|
|
|
static int grx_unblock(struct graphics_device *dev)
|
|
{
|
|
if (current_virtual_device) {
|
|
internal_error("grx_unblock called without grx_block");
|
|
return 0;
|
|
}
|
|
grx_set_graphics();
|
|
svgalib_unblock_itrm(svgalib_kbd);
|
|
current_virtual_device = grx_old_vd;
|
|
grx_old_vd = NULL;
|
|
grx_set_palette();
|
|
grx_set_clip();
|
|
grx_mouse_init();
|
|
if (current_virtual_device) current_virtual_device->redraw_handler(current_virtual_device, ¤t_virtual_device->size);
|
|
return 0;
|
|
}
|
|
|
|
struct graphics_driver grx_driver = {
|
|
cast_uchar "grx",
|
|
grx_init_driver,
|
|
init_virtual_device,
|
|
shutdown_virtual_device,
|
|
grx_shutdown_driver,
|
|
grx_emergency_shutdown,
|
|
NULL,
|
|
grx_get_driver_param,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
grx_get_empty_bitmap,
|
|
grx_register_bitmap,
|
|
grx_prepare_strip,
|
|
grx_commit_strip,
|
|
grx_unregister_bitmap,
|
|
grx_draw_bitmap,
|
|
NULL,
|
|
grx_fill_area,
|
|
grx_draw_hline,
|
|
grx_draw_vline,
|
|
grx_scroll,
|
|
grx_set_clip_area,
|
|
NULL,
|
|
grx_block,
|
|
grx_unblock,
|
|
grx_update_palette, /* set_palette */
|
|
NULL, /* get_real_colors */
|
|
NULL, /* set_title */
|
|
NULL, /* exec */
|
|
NULL, /* set_clipboard_text */
|
|
NULL, /* get_clipboard_text */
|
|
0, /* depth */
|
|
0, 0, /* size */
|
|
GD_NEED_CODEPAGE, /* flags */
|
|
NULL, /* param */
|
|
};
|
|
|
|
#endif
|