691 lines
18 KiB
C
691 lines
18 KiB
C
/** \file server/driver.c
|
|
* This code does all actions on the driver object.
|
|
*/
|
|
|
|
/* This file is part of LCDd, the lcdproc server.
|
|
*
|
|
* This file is released under the GNU General Public License.
|
|
* Refer to the COPYING file distributed with this package.
|
|
*
|
|
* Copyright (c) 2001, Joris Robijn
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#ifndef WIN32
|
|
# include <sys/errno.h>
|
|
# include <dlfcn.h>
|
|
#else
|
|
# include <windows.h>
|
|
#endif
|
|
#include <string.h>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "main.h" /* for timer */
|
|
|
|
#include "shared/report.h"
|
|
#include "shared/configfile.h"
|
|
|
|
#include "widget.h"
|
|
#include "driver.h"
|
|
#include "drivers.h"
|
|
#include "drivers/lcd.h"
|
|
/* lcd.h is used for the driver API definition */
|
|
|
|
|
|
/** property / method symbols in a Driver structure */
|
|
typedef struct driver_symbols {
|
|
const char *name; /**< symbol name */
|
|
short offset; /**< offset in Driver structure */
|
|
short required; /**< is the symbol mandatory */
|
|
} DriverSymbols;
|
|
|
|
DriverSymbols driver_symbols[] = {
|
|
{ "api_version", offsetof(Driver, api_version), 1 },
|
|
{ "stay_in_foreground", offsetof(Driver, stay_in_foreground), 1 },
|
|
{ "supports_multiple", offsetof(Driver, supports_multiple), 1 },
|
|
{ "symbol_prefix", offsetof(Driver, symbol_prefix), 1 },
|
|
{ "init", offsetof(Driver, init), 1 },
|
|
{ "close", offsetof(Driver, close), 1 },
|
|
{ "width", offsetof(Driver, width), 0 },
|
|
{ "height", offsetof(Driver, height), 0 },
|
|
{ "clear", offsetof(Driver, clear), 0 },
|
|
{ "flush", offsetof(Driver, flush), 0 },
|
|
{ "string", offsetof(Driver, string), 0 },
|
|
{ "chr", offsetof(Driver, chr), 0 },
|
|
{ "vbar", offsetof(Driver, vbar), 0 },
|
|
{ "hbar", offsetof(Driver, hbar), 0 },
|
|
{ "num", offsetof(Driver, num), 0 },
|
|
{ "heartbeat", offsetof(Driver, heartbeat), 0 },
|
|
{ "icon", offsetof(Driver, icon), 0 },
|
|
{ "cursor", offsetof(Driver, cursor), 0 },
|
|
{ "set_char", offsetof(Driver, set_char), 0 },
|
|
{ "get_free_chars", offsetof(Driver, get_free_chars), 0 },
|
|
{ "cellwidth", offsetof(Driver, cellwidth), 0 },
|
|
{ "cellheight", offsetof(Driver, cellheight), 0 },
|
|
{ "get_contrast", offsetof(Driver, get_contrast), 0 },
|
|
{ "set_contrast", offsetof(Driver, set_contrast), 0 },
|
|
{ "get_brightness", offsetof(Driver, get_brightness), 0 },
|
|
{ "set_brightness", offsetof(Driver, set_brightness), 0 },
|
|
{ "backlight", offsetof(Driver, backlight), 0 },
|
|
{ "output", offsetof(Driver, output), 0 },
|
|
{ "get_key", offsetof(Driver, get_key), 0 },
|
|
{ "get_info", offsetof(Driver, get_info), 0 },
|
|
{ NULL, 0, 0 }
|
|
};
|
|
|
|
|
|
|
|
/* Functions for the driver */
|
|
static int request_display_width(void);
|
|
static int request_display_height(void);
|
|
static int driver_store_private_ptr(Driver *driver, void *private_data);
|
|
|
|
|
|
/** Create a driver object.
|
|
* Allocate memory for the driver object, load it from file and bind its symbols.
|
|
* \param name Name under which the driver shall be kbown forther on.
|
|
* \param filename Name of the file containing the drivers object code.
|
|
* \return Pointer to the freshly created driver; \c NULL on error.
|
|
*/
|
|
Driver *
|
|
driver_load(const char *name, const char *filename)
|
|
{
|
|
Driver *driver = NULL;
|
|
int res;
|
|
|
|
report(RPT_DEBUG, "%s(name=\"%.40s\", filename=\"%.80s\")",
|
|
__FUNCTION__, name, filename);
|
|
|
|
/* fail on wrong / missing parameters */
|
|
if ((name == NULL) || (filename == NULL))
|
|
return NULL;
|
|
|
|
/* Allocate memory for new driver struct */
|
|
driver = calloc(1, sizeof(Driver));
|
|
if (driver == NULL) {
|
|
report(RPT_ERR, "%s: error allocating driver", __FUNCTION__);
|
|
return NULL;
|
|
}
|
|
|
|
/* And store its name and filename */
|
|
driver->name = malloc(strlen(name) + 1);
|
|
if (driver->name == NULL) {
|
|
report(RPT_ERR, "%s: error allocating driver name", __FUNCTION__);
|
|
free(driver);
|
|
return NULL;
|
|
}
|
|
strcpy(driver->name, name);
|
|
|
|
driver->filename = malloc(strlen(filename) + 1);
|
|
if (driver->filename == NULL) {
|
|
report(RPT_ERR, "%s: error allocating driver filename", __FUNCTION__);
|
|
free(driver->name);
|
|
free(driver);
|
|
return NULL;
|
|
}
|
|
strcpy(driver->filename, filename);
|
|
|
|
/* Load and bind the driver module and locate the symbols */
|
|
if (driver_bind_module(driver) < 0) {
|
|
report(RPT_ERR, "Driver [%.40s] binding failed", name);
|
|
free(driver->name);
|
|
free(driver->filename);
|
|
free(driver);
|
|
return NULL;
|
|
}
|
|
|
|
/* Check module version */
|
|
if (strcmp(*(driver->api_version), API_VERSION) != 0) {
|
|
report(RPT_ERR, "Driver [%.40s] is of an incompatible version", name);
|
|
driver_unbind_module(driver);
|
|
free(driver->name);
|
|
free(driver->filename);
|
|
free(driver);
|
|
return NULL;
|
|
}
|
|
|
|
/* Call the init function */
|
|
debug(RPT_DEBUG, "%s: Calling driver [%.40s] init function",
|
|
__FUNCTION__, driver->name);
|
|
|
|
res = driver->init(driver);
|
|
if (res < 0) {
|
|
report(RPT_ERR, "Driver [%.40s] init failed, return code %d",
|
|
driver->name, res);
|
|
/* Driver load failed, driver should not be added to list
|
|
* Free driver structure again
|
|
*/
|
|
driver_unbind_module(driver);
|
|
free(driver->name);
|
|
free(driver->filename);
|
|
free(driver);
|
|
return NULL;
|
|
}
|
|
|
|
debug(RPT_NOTICE, "Driver [%.40s] loaded", driver->name);
|
|
|
|
return driver;
|
|
}
|
|
|
|
|
|
/** Unload driver from memory.
|
|
* \param driver Driver to unload.
|
|
* \retval <0 Error.
|
|
* \retval 0 Success.
|
|
*/
|
|
int
|
|
driver_unload(Driver *driver)
|
|
{
|
|
debug(RPT_NOTICE, "Closing driver [%.40s]", driver->name);
|
|
|
|
/* close the driver, if its \c close method is [already] defined */
|
|
if (driver->close != NULL)
|
|
driver->close(driver);
|
|
|
|
/* unload the module */
|
|
driver_unbind_module(driver);
|
|
|
|
/* free its data */
|
|
free(driver->filename);
|
|
driver->filename = NULL;
|
|
free(driver->name);
|
|
driver->name = NULL;
|
|
free(driver);
|
|
driver = NULL;
|
|
|
|
debug(RPT_DEBUG, "%s: Driver unloaded", __FUNCTION__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Dynamically load a module and bind it to the Driver's symbols.
|
|
* \param driver Pointer to the Driver object.
|
|
* \retval <0 Error.
|
|
* \retval 0 Success.
|
|
*/
|
|
int
|
|
driver_bind_module(Driver *driver)
|
|
{
|
|
int i;
|
|
int missing_symbols = 0;
|
|
|
|
debug(RPT_DEBUG, "%s(driver=[%.40s])", __FUNCTION__, driver->name);
|
|
|
|
/* Load the module */
|
|
#ifndef WIN32
|
|
driver->module_handle = dlopen(driver->filename, RTLD_NOW);
|
|
#else
|
|
driver->module_handle = LoadLibraryEx((driver->filename), NULL, 0);
|
|
#endif
|
|
if (driver->module_handle == NULL) {
|
|
#ifndef WIN32
|
|
report(RPT_ERR, "Could not open driver module %.40s: %s",
|
|
driver->filename, dlerror());
|
|
#else
|
|
/* REVISIT: replace dlerror() */
|
|
report(RPT_ERR, "Could not open driver module %.40s.",
|
|
driver->filename);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
/* And locate the symbols */
|
|
for (i = 0; driver_symbols[i].name != NULL; i++) {
|
|
void (**p)();
|
|
|
|
p = (void(**)()) ((size_t)driver + (driver_symbols[i].offset));
|
|
*p = NULL;
|
|
|
|
/* 1) try to retrieve the symbol with the driver's symbol_prefix added */
|
|
if (driver->symbol_prefix != NULL) {
|
|
char *s = malloc(strlen(*(driver->symbol_prefix)) + strlen(driver_symbols[i].name) + 1);
|
|
strcpy(s, *(driver->symbol_prefix));
|
|
strcat(s, driver_symbols[i].name);
|
|
debug(RPT_DEBUG, "%s: finding symbol: %s", __FUNCTION__, s);
|
|
#ifndef WIN32
|
|
*p = dlsym(driver->module_handle, s);
|
|
#else
|
|
*p = (void(*)()) (GetProcAddress(driver->module_handle, s));
|
|
#endif
|
|
free(s);
|
|
}
|
|
/* 2) try to retrieve the symbol without the symbol prefix */
|
|
if (*p == NULL) {
|
|
debug(RPT_DEBUG, "%s: finding symbol: %s", __FUNCTION__, driver_symbols[i].name);
|
|
#ifndef WIN32
|
|
*p = dlsym(driver->module_handle, driver_symbols[i].name);
|
|
#else
|
|
*p = (void(*)()) (GetProcAddress(driver->module_handle, driver_symbols[i].name));
|
|
#endif
|
|
}
|
|
|
|
if (*p != NULL) {
|
|
debug(RPT_DEBUG, "%s: found symbol at: %p", __FUNCTION__, *p);
|
|
}
|
|
else {
|
|
/* Was the symbol required but not found ? */
|
|
if (driver_symbols[i].required) {
|
|
report(RPT_ERR, "Driver [%.40s] does not have required symbol: %s",
|
|
driver->name, driver_symbols[i].name);
|
|
missing_symbols++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If errors, leave now while we can :) */
|
|
if (missing_symbols > 0) {
|
|
report(RPT_ERR, "Driver [%.40s] misses %d required symbols",
|
|
driver->name, missing_symbols);
|
|
#ifndef WIN32
|
|
dlclose(driver->module_handle);
|
|
#else
|
|
FreeLibrary(driver->module_handle);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
/* Add our exported functions */
|
|
|
|
/* Config file functions */
|
|
driver->config_get_bool = config_get_bool;
|
|
driver->config_get_int = config_get_int;
|
|
driver->config_get_float = config_get_float;
|
|
driver->config_get_string = config_get_string;
|
|
driver->config_has_section = config_has_section;
|
|
driver->config_has_key = config_has_key;
|
|
|
|
/* Reporting */
|
|
driver->report = report;
|
|
|
|
/* Driver private data */
|
|
driver->store_private_ptr = driver_store_private_ptr;
|
|
|
|
/* Display size request */
|
|
driver->request_display_width = request_display_width;
|
|
driver->request_display_height = request_display_height;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Unload a Driver's module.
|
|
* \param driver Pointer to he driver object.
|
|
* \retval <0 Error.
|
|
* \retval 0 Success.
|
|
*/
|
|
int
|
|
driver_unbind_module(Driver *driver)
|
|
{
|
|
debug(RPT_DEBUG, "%s(driver=[%.40s])", __FUNCTION__, driver->name);
|
|
|
|
#ifndef WIN32
|
|
dlclose(driver->module_handle);
|
|
#else
|
|
FreeLibrary(driver->module_handle);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Determine if the driver is an output driver.
|
|
* This is done by checking whether the driver knows dimensions
|
|
* and supports the methods that write to the screen.
|
|
* \param driver Pointer to he driver object.
|
|
* \retval 0 No, it is not an output driver.
|
|
* \retval 1 Yes, it is an output driver.
|
|
*/
|
|
bool
|
|
driver_does_output(Driver *driver)
|
|
{
|
|
return (driver->width != NULL
|
|
|| driver->height != NULL
|
|
|| driver->clear != NULL
|
|
|| driver->string != NULL
|
|
|| driver->chr != NULL) ? 1 : 0;
|
|
}
|
|
|
|
|
|
/** Determine if the driver is an input driver.
|
|
* This is done by checking whether the driver supports the \c get_key method.
|
|
* \param driver Pointer to he driver object.
|
|
* \retval 0 No, it is not an input driver.
|
|
* \retval 1 Yes, it is an input driver.
|
|
*/
|
|
bool
|
|
driver_does_input(Driver *driver)
|
|
{
|
|
return (driver->get_key != NULL) ? 1 : 0;
|
|
}
|
|
|
|
|
|
/** Tell if the driver needs to stay in the foreground.
|
|
* \param driver Pointer to he driver object.
|
|
* \retval 0 No, the driver does not need to stay in the foreground.
|
|
* \retval 1 Yes, the driver needs to stay in the foreground.
|
|
*/
|
|
bool
|
|
driver_stay_in_foreground(Driver *driver)
|
|
{
|
|
return *driver->stay_in_foreground;
|
|
}
|
|
|
|
|
|
/** Tell if the driver supports multiple instances.
|
|
* \param driver Pointer to he driver object.
|
|
* \retval 0 No, it doesn't.
|
|
* \retval 1 Yes, it does.
|
|
*/
|
|
bool
|
|
driver_supports_multiple(Driver *driver)
|
|
{
|
|
return *driver->supports_multiple;
|
|
}
|
|
|
|
|
|
static int
|
|
driver_store_private_ptr(Driver *driver, void *private_data)
|
|
{
|
|
debug(RPT_DEBUG, "%s(driver=[%.40s], ptr=%p)",
|
|
__FUNCTION__, driver->name, private_data);
|
|
|
|
driver->private_data = private_data;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
request_display_width(void)
|
|
{
|
|
if (!display_props)
|
|
return 0;
|
|
return display_props->width;
|
|
}
|
|
|
|
|
|
static int
|
|
request_display_height(void)
|
|
{
|
|
if (!display_props)
|
|
return 0;
|
|
return display_props->height;
|
|
}
|
|
|
|
|
|
/** Draw a vertical bar bottom-up.
|
|
* Fallback for the driver's \c vbar method if the driver does not provide one.
|
|
* \param drv Pointer to driver structure.
|
|
* \param x Horizontal character position (column) of the starting point.
|
|
* \param y Vertical character position (row) of the starting point.
|
|
* \param len Number of characters that the bar is high at 100%
|
|
* \param promille Current height level of the bar in promille.
|
|
* \param options Options (currently unused).
|
|
*/
|
|
void
|
|
driver_alt_vbar(Driver *drv, int x, int y, int len, int promille, int options)
|
|
{
|
|
int pos;
|
|
|
|
debug(RPT_DEBUG, "%s(drv=[%.40s], x=%d, y=%d, len=%d, promille=%d, options=%d)",
|
|
__FUNCTION__, drv->name, x, y, len, promille, options);
|
|
|
|
/* if the driver does not support output, do nothing */
|
|
if (drv->chr == NULL)
|
|
return;
|
|
|
|
for (pos = 0; pos < len; pos++) {
|
|
if (2 * pos < ((long) promille * len / 500 + 1)) {
|
|
drv->chr(drv, x, y-pos, '|');
|
|
} else {
|
|
; /* print nothing */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/** Draw a horizontal bar to the right.
|
|
* Fallback for the driver's \c hbar method if the driver does not provide one.
|
|
* \param drv Pointer to driver structure.
|
|
* \param x Horizontal character position (column) of the starting point.
|
|
* \param y Vertical character position (row) of the starting point.
|
|
* \param len Number of characters that the bar is long at 100%
|
|
* \param promille Current length level of the bar in promille.
|
|
* \param options Options (currently unused).
|
|
*/
|
|
void
|
|
driver_alt_hbar(Driver *drv, int x, int y, int len, int promille, int options)
|
|
{
|
|
int pos;
|
|
|
|
debug(RPT_DEBUG, "%s(drv=[%.40s], x=%d, y=%d, len=%d, promille=%d, options=%d)",
|
|
__FUNCTION__, drv->name, x, y, len, promille, options);
|
|
|
|
/* if the driver does not support output, do nothing */
|
|
if (drv->chr == NULL)
|
|
return;
|
|
|
|
for (pos = 0; pos < len; pos++) {
|
|
if (2 * pos < ((long) promille * len / 500 + 1)) {
|
|
drv->chr(drv, x+pos, y, '-');
|
|
} else {
|
|
; /* print nothing */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/** Write a big number to the screen.
|
|
* Fallback for the driver's \c num method if the driver does not provide one.
|
|
* \param drv Pointer to driver structure.
|
|
* \param x Horizontal character position (column).
|
|
* \param num Character to write (0 - 10 with 10 representing ':')
|
|
*/
|
|
void
|
|
driver_alt_num(Driver *drv, int x, int num)
|
|
{
|
|
/* Ugly code extracted by David GLAUDE from lcdm001.c ;)*/
|
|
/* Moved to driver.c by Joris Robijn */
|
|
static char num_map[][4][4] = {
|
|
{ /* 0 */
|
|
" _ ",
|
|
"| |",
|
|
"|_|",
|
|
" " },
|
|
{ /* 1 */
|
|
" ",
|
|
" |",
|
|
" |",
|
|
" " },
|
|
{ /* 2 */
|
|
" _ ",
|
|
" _|",
|
|
"|_ ",
|
|
" " },
|
|
{ /* 3 */
|
|
" _ ",
|
|
" _|",
|
|
" _|",
|
|
" " },
|
|
{ /* 4 */
|
|
" ",
|
|
"|_|",
|
|
" |",
|
|
" " },
|
|
{ /* 5 */
|
|
" _ ",
|
|
"|_ ",
|
|
" _|",
|
|
" " },
|
|
{ /* 6 */
|
|
" _ ",
|
|
"|_ ",
|
|
"|_|",
|
|
" " },
|
|
{ /* 7 */
|
|
" _ ",
|
|
" |",
|
|
" |",
|
|
" " },
|
|
{ /* 8 */
|
|
" _ ",
|
|
"|_|",
|
|
"|_|",
|
|
" " },
|
|
{ /* 9 */
|
|
" _ ",
|
|
"|_|",
|
|
" _|",
|
|
" " },
|
|
{ /* colon */
|
|
" ",
|
|
".",
|
|
".",
|
|
" " }
|
|
};
|
|
/* End of ugly code ;) by Rene Wagner */
|
|
/* I like this code ! Joris */
|
|
|
|
int y, dx;
|
|
|
|
debug(RPT_DEBUG, "%s(drv=[%.40s], x=%d, num=%d)",
|
|
__FUNCTION__, drv->name, x, num);
|
|
|
|
if ((num < 0) || (num > 10))
|
|
return;
|
|
/* if the driver does not support output, do nothing */
|
|
if (drv->chr == NULL)
|
|
return;
|
|
|
|
for (y = 0; y < 4; y++)
|
|
for (dx = 0; num_map[num][y][dx] != '\0'; dx++)
|
|
drv->chr(drv, x + dx, y+1, num_map[num][y][dx]);
|
|
}
|
|
|
|
|
|
/** Show the heartbeat.
|
|
* Fallback for the driver's \c heartbeat method if the driver does not provide one.
|
|
* \param drv Pointer to driver structure.
|
|
* \param state Current heartbeat state.
|
|
*/
|
|
void
|
|
driver_alt_heartbeat(Driver *drv, int state)
|
|
{
|
|
int icon;
|
|
|
|
debug(RPT_DEBUG, "%s(drv=[%.40s], state=%d)",
|
|
__FUNCTION__, drv->name, state);
|
|
|
|
if (state == HEARTBEAT_OFF)
|
|
return;
|
|
/* Don't display anything */
|
|
|
|
/* if the driver does not support output, do nothing */
|
|
if (drv->width == NULL)
|
|
return;
|
|
|
|
/* Hmm, is this a good method ?
|
|
* Or should we use clock() ? Or ftime ? Or gettimeofday ?
|
|
*/
|
|
icon = (timer & 5) ? ICON_HEART_FILLED : ICON_HEART_OPEN;
|
|
|
|
if (drv->icon)
|
|
drv->icon(drv, drv->width(drv), 1, icon);
|
|
else
|
|
driver_alt_icon(drv, drv->width(drv), 1, icon);
|
|
}
|
|
|
|
|
|
/** Place an icon on the screen.
|
|
* Fallback for the driver's \c icon method, in case either the driver does not
|
|
* provide one or the driver's method indicates the icon needs to be handled
|
|
* by the server core.
|
|
* \param drv Pointer to driver structure.
|
|
* \param x Horizontal character position (column).
|
|
* \param y Vertical character position (row).
|
|
* \param icon synbolic value representing the icon.
|
|
*/
|
|
void
|
|
driver_alt_icon(Driver *drv, int x, int y, int icon)
|
|
{
|
|
char ch1 = '?';
|
|
char ch2 = '\0';
|
|
|
|
debug(RPT_DEBUG, "%s(drv=[%.40s], x=%d, y=%d, icon=ICON_%s)",
|
|
__FUNCTION__, drv->name, x, y, widget_icon_to_iconname(icon));
|
|
|
|
/* if the driver does not support output, do nothing */
|
|
if (drv->chr == NULL)
|
|
return;
|
|
|
|
switch (icon) {
|
|
case ICON_BLOCK_FILLED: ch1 = '#'; break;
|
|
case ICON_HEART_OPEN: ch1 = '-'; break;
|
|
case ICON_HEART_FILLED: ch1 = '#'; break;
|
|
case ICON_ARROW_UP: ch1 = '^'; break;
|
|
case ICON_ARROW_DOWN: ch1 = 'v'; break;
|
|
case ICON_ARROW_LEFT: ch1 = '<'; break;
|
|
case ICON_ARROW_RIGHT: ch1 = '>'; break;
|
|
case ICON_CHECKBOX_OFF: ch1 = 'N'; break;
|
|
case ICON_CHECKBOX_ON: ch1 = 'Y'; break;
|
|
case ICON_CHECKBOX_GRAY: ch1 = 'o'; break;
|
|
case ICON_SELECTOR_AT_LEFT: ch1 = '>'; break;
|
|
case ICON_SELECTOR_AT_RIGHT: ch1 = '<'; break;
|
|
case ICON_ELLIPSIS: ch1 = '_'; break;
|
|
case ICON_STOP: ch1 = '['; ch2 = ']'; break;
|
|
case ICON_PAUSE: ch1 = '|'; ch2 = '|'; break;
|
|
case ICON_PLAY: ch1 = '>'; ch2 = ' '; break;
|
|
case ICON_PLAYR: ch1 = '<'; ch2 = ' '; break;
|
|
case ICON_FF: ch1 = '>'; ch2 = '>'; break;
|
|
case ICON_FR: ch1 = '<'; ch2 = '<'; break;
|
|
case ICON_NEXT: ch1 = '>'; ch2 = '|'; break;
|
|
case ICON_PREV: ch1 = '|'; ch2 = '<'; break;
|
|
case ICON_REC: ch1 = '('; ch2 = ')'; break;
|
|
}
|
|
|
|
drv->chr(drv, x, y, ch1);
|
|
if (ch2 != '\0')
|
|
drv->chr(drv, x+1, y, ch2);
|
|
}
|
|
|
|
|
|
/** Set cursor position and state.
|
|
* Fallback for the driver's \c cursor method if the driver does not provide one.
|
|
* \param drv Pointer to driver structure.
|
|
* \param x Horizontal cursor position (column).
|
|
* \param y Vertical cursor position (row).
|
|
* \param state New cursor state.
|
|
*/
|
|
void driver_alt_cursor(Driver *drv, int x, int y, int state)
|
|
{
|
|
/* Same question about timer in this function... */
|
|
|
|
debug(RPT_DEBUG, "%s(drv=[%.40s], x=%d, y=%d, state=%d)",
|
|
__FUNCTION__, drv->name, x, y, state);
|
|
|
|
switch (state) {
|
|
case CURSOR_BLOCK:
|
|
case CURSOR_DEFAULT_ON:
|
|
if ((timer & 2) && (drv->chr != NULL)) {
|
|
if (drv->icon != NULL) {
|
|
drv->icon(drv, x, y, ICON_BLOCK_FILLED);
|
|
} else {
|
|
driver_alt_icon(drv, x, y, ICON_BLOCK_FILLED);
|
|
}
|
|
}
|
|
break;
|
|
case CURSOR_UNDER:
|
|
if ((timer & 2) && (drv->chr != NULL)) {
|
|
drv->chr(drv, x, y, '_');
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|