blob: 29f2080cec818d76860ad1a86aa2fd032ef97440 [file] [log] [blame] [edit]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include "status.h"
#include "timing.h"
#include "update.h"
#include "brl_dots.h"
#include "brl_utils.h"
#include "scr_special.h"
#include "core.h"
#include "prefs.h"
#include "ttb.h"
static void
renderCharacter (unsigned char *cell, char character) {
*cell = convertCharacterToDots(textTable, character);
}
static void
renderDigitUpper (unsigned char *cell, int digit) {
*cell |= portraitDigits[digit % 10];
}
static void
renderDigitLower (unsigned char *cell, int digit) {
*cell |= toLowerDigit(portraitDigits[digit % 10]);
}
static void
renderNumberVertical (unsigned char *cell, int number) {
renderDigitUpper(cell, number/10);
renderDigitLower(cell, number);
}
static void
renderNumberUpper2 (unsigned char *cells, int number) {
renderDigitUpper(&cells[0], number/10);
renderDigitUpper(&cells[1], number);
}
static void
renderNumberLower2 (unsigned char *cells, int number) {
renderDigitLower(&cells[0], number/10);
renderDigitLower(&cells[1], number);
}
static void
renderNumbers2 (unsigned char *cells, int upper, int lower) {
renderNumberUpper2(&cells[0], upper);
renderNumberLower2(&cells[0], lower);
}
static void
renderNumberUpper3 (unsigned char *cells, int number) {
renderDigitUpper(&cells[0], number/100);
renderDigitUpper(&cells[1], number/10);
renderDigitUpper(&cells[2], number);
}
static void
renderNumberLower3 (unsigned char *cells, int number) {
renderDigitLower(&cells[0], number/100);
renderDigitLower(&cells[1], number/10);
renderDigitLower(&cells[2], number);
}
static void
renderNumbers3 (unsigned char *cells, int upper, int lower) {
renderNumberUpper3(&cells[0], upper);
renderNumberLower3(&cells[0], lower);
}
static void
renderCoordinates2 (unsigned char *cells, int column, int row) {
renderNumbers2(&cells[0], row, column);
}
static void
renderCoordinates3 (unsigned char *cells, int column, int row) {
renderNumbers3(&cells[0], row, column);
}
static void
renderCoordinatesAlphabetic (unsigned char *cell, int column, int row) {
/* the coordinates are presented as an underlined letter as the Alva DOS TSR */
if (!SCR_COORDINATES_OK(column, row)) {
*cell = convertCharacterToDots(textTable, WC_C('z'));
} else {
const int32_t height = 25;
const int32_t frequency = row / height;
if (frequency) {
const int32_t interval = NSECS_PER_SEC / (frequency * 2);
TimeValue time;
getMonotonicTime(&time);
scheduleUpdateIn("alva status field",
(((interval - (time.nanoseconds % interval)) / NSECS_PER_MSEC) + 1));
if (!((time.nanoseconds / interval) % 2)) {
*cell = 0;
return;
}
}
*cell = convertCharacterToDots(textTable, ((row % height) + WC_C('a')))
| ((column / textCount) << 6);
}
}
typedef void (*RenderStatusField) (unsigned char *cells);
static void
renderStatusField_cursorColumn (unsigned char *cells) {
renderNumberVertical(cells, SCR_COLUMN_NUMBER(scr.posx));
}
static void
renderStatusField_cursorRow (unsigned char *cells) {
renderNumberVertical(cells, SCR_ROW_NUMBER(scr.posy));
}
static void
renderStatusField_windowColumn (unsigned char *cells) {
renderNumberVertical(cells, SCR_COLUMN_NUMBER(ses->winx));
}
static void
renderStatusField_windowRow (unsigned char *cells) {
renderNumberVertical(cells, SCR_ROW_NUMBER(ses->winy));
}
static void
renderStatusField_cursorCoordinates2 (unsigned char *cells) {
renderCoordinates2(cells, SCR_COLUMN_NUMBER(scr.posx), SCR_ROW_NUMBER(scr.posy));
}
static void
renderStatusField_windowCoordinates2 (unsigned char *cells) {
renderCoordinates2(cells, SCR_COLUMN_NUMBER(ses->winx), SCR_ROW_NUMBER(ses->winy));
}
static void
renderStatusField_cursorCoordinates3 (unsigned char *cells) {
renderCoordinates3(cells, SCR_COLUMN_NUMBER(scr.posx), SCR_ROW_NUMBER(scr.posy));
}
static void
renderStatusField_windowCoordinates3 (unsigned char *cells) {
renderCoordinates3(cells, SCR_COLUMN_NUMBER(ses->winx), SCR_ROW_NUMBER(ses->winy));
}
static void
renderStatusField_cursorAndWindowColumn2 (unsigned char *cells) {
renderNumbers2(cells,
SCR_COLUMN_NUMBER(scr.posx),
SCR_COLUMN_NUMBER(ses->winx)
);
}
static void
renderStatusField_cursorAndWindowRow2 (unsigned char *cells) {
renderNumbers2(cells,
SCR_ROW_NUMBER(scr.posy),
SCR_ROW_NUMBER(ses->winy)
);
}
static void
renderStatusField_cursorAndWindowColumn3 (unsigned char *cells) {
renderNumbers3(cells,
SCR_COLUMN_NUMBER(scr.posx),
SCR_COLUMN_NUMBER(ses->winx)
);
}
static void
renderStatusField_cursorAndWindowRow3 (unsigned char *cells) {
renderNumbers3(cells,
SCR_ROW_NUMBER(scr.posy),
SCR_ROW_NUMBER(ses->winy)
);
}
static void
renderStatusField_screenNumber (unsigned char *cells) {
char character =
isSpecialScreen(SCR_HELP) ? 'h':
isSpecialScreen(SCR_MENU) ? 'm':
isSpecialScreen(SCR_FROZEN)? 'f':
0;
if (character) {
renderCharacter(cells, character);
} else {
renderNumberVertical(cells, scr.number);
}
}
static void
renderStatusField_stateDots (unsigned char *cells) {
cells[0] = (isSpecialScreen(SCR_FROZEN) ? BRL_DOT_1: 0)
| (prefs.showScreenCursor ? BRL_DOT_4: 0)
| (ses->displayMode ? BRL_DOT_2: 0)
| (prefs.showAttributes ? BRL_DOT_5: 0)
| (prefs.alertTunes ? BRL_DOT_3: 0)
| (prefs.brailleTypingMode ? BRL_DOT_6: 0)
| (ses->trackScreenCursor ? BRL_DOT_7: 0)
| (prefs.brailleKeyboardEnabled ? BRL_DOT_8: 0)
;
}
static void
renderStatusField_stateLetter (unsigned char *cells) {
renderCharacter(cells,
ses->displayMode ? WC_C('a'):
isSpecialScreen(SCR_HELP) ? WC_C('h'):
isSpecialScreen(SCR_MENU) ? WC_C('m'):
isSpecialScreen(SCR_FROZEN) ? WC_C('f'):
ses->trackScreenCursor ? WC_C('t'):
WC_C(' ')
);
}
static void
renderStatusField_time (unsigned char *cells) {
TimeValue value;
getCurrentTime(&value);
scheduleUpdateIn("time status field", millisecondsTillNextMinute(&value));
TimeComponents components;
expandTimeValue(&value, &components);
renderNumbers2(cells, components.hour, components.minute);
}
static void
renderStatusField_alphabeticWindowCoordinates (unsigned char *cells) {
renderCoordinatesAlphabetic(cells, ses->winx, ses->winy);
}
static void
renderStatusField_alphabeticCursorCoordinates (unsigned char *cells) {
renderCoordinatesAlphabetic(cells, scr.posx, scr.posy);
}
static void
renderStatusField_generic (unsigned char *cells) {
cells[GSC_FIRST] = GSC_MARKER;
cells[gscBrailleWindowColumn] = SCR_COLUMN_NUMBER(ses->winx);
cells[gscBrailleWindowRow] = SCR_ROW_NUMBER(ses->winy);
cells[gscScreenCursorColumn] = SCR_COLUMN_NUMBER(scr.posx);
cells[gscScreenCursorRow] = SCR_ROW_NUMBER(scr.posy);
cells[gscScreenNumber] = scr.number;
cells[gscFrozenScreen] = isSpecialScreen(SCR_FROZEN);
cells[gscDisplayMode] = ses->displayMode;
cells[gscSixDotComputerBraille] = isSixDotComputerBraille();
cells[gscContractedBraille] = isContractedBraille();
cells[gscSlidingBrailleWindow] = prefs.slidingBrailleWindow;
cells[gscSkipIdenticalLines] = prefs.skipIdenticalLines;
cells[gscSkipBlankBrailleWindows] = prefs.skipBlankBrailleWindows;
cells[gscShowScreenCursor] = prefs.showScreenCursor;
cells[gscHideScreenCursor] = ses->hideScreenCursor;
cells[gscTrackScreenCursor] = ses->trackScreenCursor;
cells[gscScreenCursorStyle] = prefs.screenCursorStyle;
cells[gscBlinkingScreenCursor] = prefs.blinkingScreenCursor;
cells[gscShowAttributes] = prefs.showAttributes;
cells[gscBlinkingAttributes] = prefs.blinkingAttributes;
cells[gscBlinkingCapitals] = prefs.blinkingCapitals;
cells[gscAlertTunes] = prefs.alertTunes;
cells[gscAutorepeat] = prefs.autorepeatEnabled;
cells[gscAutospeak] = prefs.autospeak;
cells[gscBrailleTypingMode] = prefs.brailleTypingMode;
}
static void
renderStatusField_space (unsigned char *cells) {
cells[0] = 0;
}
typedef struct {
RenderStatusField render;
unsigned char length;
} StatusFieldEntry;
static const StatusFieldEntry statusFieldTable[] = {
[sfEnd] = {
.render = NULL,
.length = 0
}
,
[sfWindowCoordinates2] = {
.render = renderStatusField_windowCoordinates2,
.length = 2
}
,
[sfWindowColumn] = {
.render = renderStatusField_windowColumn,
.length = 1
}
,
[sfWindowRow] = {
.render = renderStatusField_windowRow,
.length = 1
}
,
[sfCursorCoordinates2] = {
.render = renderStatusField_cursorCoordinates2,
.length = 2
}
,
[sfCursorColumn] = {
.render = renderStatusField_cursorColumn,
.length = 1
}
,
[sfCursorRow] = {
.render = renderStatusField_cursorRow,
.length = 1
}
,
[sfCursorAndWindowColumn2] = {
.render = renderStatusField_cursorAndWindowColumn2,
.length = 2
}
,
[sfCursorAndWindowRow2] = {
.render = renderStatusField_cursorAndWindowRow2,
.length = 2
}
,
[sfScreenNumber] = {
.render = renderStatusField_screenNumber,
.length = 1
}
,
[sfStateDots] = {
.render = renderStatusField_stateDots,
.length = 1
}
,
[sfStateLetter] = {
.render = renderStatusField_stateLetter,
.length = 1
}
,
[sfTime] = {
.render = renderStatusField_time,
.length = 2
}
,
[sfAlphabeticWindowCoordinates] = {
.render = renderStatusField_alphabeticWindowCoordinates,
.length = 1
}
,
[sfAlphabeticCursorCoordinates] = {
.render = renderStatusField_alphabeticCursorCoordinates,
.length = 1
}
,
[sfGeneric] = {
.render = renderStatusField_generic,
.length = GSC_COUNT
},
[sfCursorCoordinates3] = {
.render = renderStatusField_cursorCoordinates3,
.length = 3
}
,
[sfWindowCoordinates3] = {
.render = renderStatusField_windowCoordinates3,
.length = 3
}
,
[sfCursorAndWindowColumn3] = {
.render = renderStatusField_cursorAndWindowColumn3,
.length = 3
}
,
[sfCursorAndWindowRow3] = {
.render = renderStatusField_cursorAndWindowRow3,
.length = 3
}
,
[sfSpace] = {
.render = renderStatusField_space,
.length = 1
},
};
static const unsigned int statusFieldCount = ARRAY_COUNT(statusFieldTable);
unsigned int
getStatusFieldsLength (const unsigned char *fields) {
unsigned int length = 0;
while (*fields != sfEnd) length += statusFieldTable[*fields++].length;
return length;
}
void
renderStatusFields (const unsigned char *fields, unsigned char *cells) {
while (*fields != sfEnd) {
StatusField field = *fields++;
if (field < statusFieldCount) {
const StatusFieldEntry *sf = &statusFieldTable[field];
sf->render(cells);
cells += sf->length;
}
}
}