#include <xterm.h>
#include <X11/Xatom.h>
#if defined(HAVE_LIB_XAW)
#include <X11/Xaw/Scrollbar.h>
#elif defined(HAVE_LIB_XAW3D)
#include <X11/Xaw3d/Scrollbar.h>
#elif defined(HAVE_LIB_NEXTAW)
#include <X11/neXtaw/Scrollbar.h>
#elif defined(HAVE_LIB_XAWPLUS)
#include <X11/XawPlus/Scrollbar.h>
#endif
#if defined(HAVE_XKBQUERYEXTENSION) && defined(HAVE_X11_XKBLIB_H) && defined(HAVE_X11_EXTENSIONS_XKB_H)
#include <X11/extensions/XKB.h>
#include <X11/XKBlib.h>
#else
#undef HAVE_XKBQUERYEXTENSION
#endif
#include <data.h>
#include <error.h>
#include <menu.h>
#include <xcharmouse.h>
#include <xstrings.h>
#define SCROLLBAR_BORDER(xw) (TScreenOf(xw)->scrollBarBorder)
#if OPT_TOOLBAR
#define ScrollBarBorder(xw) (BorderWidth(xw) ? SCROLLBAR_BORDER(xw) : 0)
#else
#define ScrollBarBorder(xw) SCROLLBAR_BORDER(xw)
#endif
static void ScrollTextTo PROTO_XT_CALLBACK_ARGS;
static void ScrollTextUpDownBy PROTO_XT_CALLBACK_ARGS;
void
DoResizeScreen(XtermWidget xw)
{
TScreen *screen = TScreenOf(xw);
int border = 2 * screen->border;
int min_wide = border + screen->fullVwin.sb_info.width;
int min_high = border;
XtGeometryResult geomreqresult;
Dimension reqWidth, reqHeight, repWidth, repHeight;
#ifndef NO_ACTIVE_ICON
VTwin *saveWin = WhichVWin(screen);
WhichVWin(screen) = &screen->fullVwin;
#endif
TRACE(("DoResizeScreen\n"));
#if 1
TRACE(("%s@%d -- ", __FILE__, __LINE__));
TRACE_WM_HINTS(xw);
getXtermSizeHints(xw);
xtermSizeHints(xw, ScrollbarWidth(screen));
xw->hints.width = MaxCols(screen) * FontWidth(screen) + xw->hints.min_width;
xw->hints.height = MaxRows(screen) * FontHeight(screen) + xw->hints.min_height;
#if OPT_MAXIMIZE
if (screen->fullscreen) {
xw->hints.width_inc = 1;
xw->hints.height_inc = 1;
}
#endif
#endif
XSetWMNormalHints(screen->display, VShellWindow(xw), &xw->hints);
reqWidth = (Dimension) (MaxCols(screen) * FontWidth(screen) + min_wide);
reqHeight = (Dimension) (MaxRows(screen) * FontHeight(screen) + min_high);
#if OPT_MAXIMIZE
if (screen->fullscreen) {
Screen *xscreen = DefaultScreenOfDisplay(xw->screen.display);
reqWidth = (Dimension) WidthOfScreen(xscreen);
reqHeight = (Dimension) HeightOfScreen(xscreen);
ScreenResize(xw, reqWidth, reqHeight, &xw->flags);
}
#endif
TRACE(("...requesting screensize chars %dx%d, pixels %dx%d\n",
MaxRows(screen),
MaxCols(screen),
reqHeight, reqWidth));
geomreqresult = REQ_RESIZE((Widget) xw, reqWidth, reqHeight,
&repWidth, &repHeight);
if (geomreqresult == XtGeometryAlmost) {
TRACE(("...almost, retry screensize %dx%d\n", repHeight, repWidth));
geomreqresult = REQ_RESIZE((Widget) xw, repWidth,
repHeight, NULL, NULL);
}
if (geomreqresult != XtGeometryYes) {
TRACE(("...still no (%d) - resize the core-class\n", geomreqresult));
xw->core.widget_class->core_class.resize((Widget) xw);
}
#if 1
if (xw->hints.flags
&& repHeight
&& repWidth) {
xw->hints.height = repHeight;
xw->hints.width = repWidth;
TRACE_HINTS(&xw->hints);
XSetWMNormalHints(screen->display, VShellWindow(xw), &xw->hints);
}
#endif
XSync(screen->display, False);
if (XtAppPending(app_con))
xevents();
#ifndef NO_ACTIVE_ICON
WhichVWin(screen) = saveWin;
#endif
}
static Widget
CreateScrollBar(XtermWidget xw, int x, int y, int height)
{
Widget result;
Arg args[6];
XtSetArg(args[0], XtNx, x);
XtSetArg(args[1], XtNy, y);
XtSetArg(args[2], XtNheight, height);
XtSetArg(args[3], XtNreverseVideo, xw->misc.re_verse);
XtSetArg(args[4], XtNorientation, XtorientVertical);
XtSetArg(args[5], XtNborderWidth, ScrollBarBorder(xw));
result = XtCreateWidget("scrollbar", scrollbarWidgetClass,
(Widget) xw, args, XtNumber(args));
XtAddCallback(result, XtNscrollProc, ScrollTextUpDownBy, 0);
XtAddCallback(result, XtNjumpProc, ScrollTextTo, 0);
return (result);
}
void
ScrollBarReverseVideo(Widget scrollWidget)
{
XtermWidget xw = getXtermWidget(scrollWidget);
if (xw != 0) {
SbInfo *sb = &(TScreenOf(xw)->fullVwin.sb_info);
Arg args[4];
Cardinal nargs = XtNumber(args);
if (sb->rv_cached == False) {
XtSetArg(args[0], XtNbackground, &(sb->bg));
XtSetArg(args[1], XtNforeground, &(sb->fg));
XtSetArg(args[2], XtNborderColor, &(sb->bdr));
XtSetArg(args[3], XtNborderPixmap, &(sb->bdpix));
XtGetValues(scrollWidget, args, nargs);
sb->rv_cached = True;
sb->rv_active = 0;
}
sb->rv_active = !(sb->rv_active);
XtSetArg(args[!(sb->rv_active)], XtNbackground, sb->bg);
XtSetArg(args[(sb->rv_active)], XtNforeground, sb->fg);
nargs = 2;
if (sb->bdpix == XtUnspecifiedPixmap) {
if (sb->rv_active) {
XtSetArg(args[2], XtNborderColor, args[1].value);
} else {
XtSetArg(args[2], XtNborderColor, sb->bdr);
}
nargs = 3;
}
XtSetValues(scrollWidget, args, nargs);
}
}
void
ScrollBarDrawThumb(Widget scrollWidget)
{
XtermWidget xw = getXtermWidget(scrollWidget);
if (xw != 0) {
TScreen *screen = TScreenOf(xw);
int thumbTop, thumbHeight, totalHeight;
thumbTop = ROW2INX(screen, screen->savedlines);
thumbHeight = MaxRows(screen);
totalHeight = thumbHeight + screen->savedlines;
XawScrollbarSetThumb(scrollWidget,
((float) thumbTop) / (float) totalHeight,
((float) thumbHeight) / (float) totalHeight);
}
}
void
ResizeScrollBar(XtermWidget xw)
{
TScreen *screen = TScreenOf(xw);
if (screen->scrollWidget != 0) {
int height = screen->fullVwin.height + screen->border * 2;
int width = screen->scrollWidget->core.width;
int ypos = -ScrollBarBorder(xw);
#ifdef SCROLLBAR_RIGHT
int xpos = ((xw->misc.useRight)
? (screen->fullVwin.fullwidth -
screen->scrollWidget->core.width -
BorderWidth(screen->scrollWidget))
: -ScrollBarBorder(xw));
#else
int xpos = -ScrollBarBorder(xw);
#endif
TRACE(("ResizeScrollBar at %d,%d %dx%d\n", ypos, xpos, height, width));
XtConfigureWidget(
screen->scrollWidget,
(Position) xpos,
(Position) ypos,
(Dimension) width,
(Dimension) height,
BorderWidth(screen->scrollWidget));
ScrollBarDrawThumb(screen->scrollWidget);
}
}
void
WindowScroll(XtermWidget xw, int top, Bool always GCC_UNUSED)
{
TScreen *screen = TScreenOf(xw);
int i, lines;
int scrolltop, scrollheight, refreshtop;
#if OPT_SCROLL_LOCK
if (screen->allowScrollLock && (screen->scroll_lock && !always)) {
if (screen->scroll_dirty) {
screen->scroll_dirty = False;
ScrnRefresh(xw, 0, 0, MaxRows(screen), MaxCols(screen), False);
}
} else
#endif
{
if (top < -screen->savedlines) {
top = -screen->savedlines;
} else if (top > 0) {
top = 0;
}
if ((i = screen->topline - top) != 0) {
if (screen->cursor_state)
HideCursor();
lines = i > 0 ? i : -i;
if (lines > MaxRows(screen))
lines = MaxRows(screen);
scrollheight = screen->max_row - lines + 1;
if (i > 0)
refreshtop = scrolltop = 0;
else {
scrolltop = lines;
refreshtop = scrollheight;
}
scrolling_copy_area(xw, scrolltop, scrollheight, -i);
screen->topline = top;
ScrollSelection(screen, i, True);
XClearArea(
screen->display,
VWindow(screen),
OriginX(screen),
OriginY(screen) + refreshtop * FontHeight(screen),
(unsigned) Width(screen),
(unsigned) (lines * FontHeight(screen)),
False);
ScrnRefresh(xw, refreshtop, 0, lines, MaxCols(screen), False);
#if OPT_BLINK_CURS || OPT_BLINK_TEXT
RestartBlinking(screen);
#endif
}
}
ScrollBarDrawThumb(screen->scrollWidget);
}
#ifdef SCROLLBAR_RIGHT
void
updateRightScrollbar(XtermWidget xw)
{
TScreen *screen = TScreenOf(xw);
if (xw->misc.useRight
&& screen->fullVwin.fullwidth < xw->core.width)
XtVaSetValues(screen->scrollWidget,
XtNx, screen->fullVwin.fullwidth - BorderWidth(screen->scrollWidget),
(XtPointer) 0);
}
#endif
void
ScrollBarOn(XtermWidget xw, Bool init)
{
TScreen *screen = TScreenOf(xw);
if (screen->fullVwin.sb_info.width || IsIcon(screen))
return;
TRACE(("ScrollBarOn(init %s)\n", BtoS(init)));
if (init) {
if (screen->scrollWidget == 0) {
screen->scrollWidget = CreateScrollBar(xw,
-ScrollBarBorder(xw),
-ScrollBarBorder(xw),
5);
if (screen->scrollWidget == NULL) {
Bell(xw, XkbBI_MinorError, 0);
}
}
} else if (!screen->scrollWidget || !XtIsRealized((Widget) xw)) {
Bell(xw, XkbBI_MinorError, 0);
Bell(xw, XkbBI_MinorError, 0);
} else {
ResizeScrollBar(xw);
xtermAddInput(screen->scrollWidget);
XtRealizeWidget(screen->scrollWidget);
TRACE_TRANS("scrollbar", screen->scrollWidget);
screen->fullVwin.sb_info.rv_cached = False;
screen->fullVwin.sb_info.width = (screen->scrollWidget->core.width
+ BorderWidth(screen->scrollWidget));
TRACE(("setting scrollbar width %d = %d + %d\n",
screen->fullVwin.sb_info.width,
screen->scrollWidget->core.width,
BorderWidth(screen->scrollWidget)));
ScrollBarDrawThumb(screen->scrollWidget);
DoResizeScreen(xw);
#ifdef SCROLLBAR_RIGHT
updateRightScrollbar(xw);
#endif
XtMapWidget(screen->scrollWidget);
update_scrollbar();
if (screen->visbuf) {
xtermClear(xw);
Redraw();
}
}
}
void
ScrollBarOff(XtermWidget xw)
{
TScreen *screen = TScreenOf(xw);
if (!screen->fullVwin.sb_info.width || IsIcon(screen))
return;
TRACE(("ScrollBarOff\n"));
if (XtIsRealized((Widget) xw)) {
XtUnmapWidget(screen->scrollWidget);
screen->fullVwin.sb_info.width = 0;
DoResizeScreen(xw);
update_scrollbar();
if (screen->visbuf) {
xtermClear(xw);
Redraw();
}
} else {
Bell(xw, XkbBI_MinorError, 0);
}
}
void
ToggleScrollBar(XtermWidget xw)
{
TScreen *screen = TScreenOf(xw);
if (IsIcon(screen)) {
Bell(xw, XkbBI_MinorError, 0);
} else {
TRACE(("ToggleScrollBar{{\n"));
if (screen->fullVwin.sb_info.width) {
ScrollBarOff(xw);
} else {
ScrollBarOn(xw, False);
}
update_scrollbar();
TRACE(("...ToggleScrollBar}}\n"));
}
}
static void
ScrollTextTo(
Widget scrollbarWidget,
XtPointer client_data GCC_UNUSED,
XtPointer call_data)
{
XtermWidget xw = getXtermWidget(scrollbarWidget);
if (xw != 0) {
float *topPercent = (float *) call_data;
TScreen *screen = TScreenOf(xw);
int thumbTop;
int newTopLine;
thumbTop = (int) (*topPercent
* (float) (screen->savedlines + MaxRows(screen)));
newTopLine = thumbTop - screen->savedlines;
WindowScroll(xw, newTopLine, True);
}
}
static void
ScrollTextUpDownBy(
Widget scrollbarWidget,
XtPointer client_data GCC_UNUSED,
XtPointer call_data)
{
XtermWidget xw = getXtermWidget(scrollbarWidget);
if (xw != 0) {
long pixels = (long) call_data;
TScreen *screen = TScreenOf(xw);
int rowOnScreen, newTopLine;
rowOnScreen = (int) (pixels / FontHeight(screen));
if (rowOnScreen == 0) {
if (pixels < 0)
rowOnScreen = -1;
else if (pixels > 0)
rowOnScreen = 1;
}
newTopLine = ROW2INX(screen, rowOnScreen);
WindowScroll(xw, newTopLine, True);
}
}
static int
CompareWidths(const char *a, const char *b, int *modifier)
{
int result;
char ca, cb;
*modifier = 0;
if (!a || !b)
return 0;
for (;;) {
ca = x_toupper(*a);
cb = x_toupper(*b);
if (ca != cb || ca == '\0')
break;
a++, b++;
}
if (cb != '\0')
return 0;
if (ca == 'S')
ca = *++a;
switch (ca) {
case '+':
case '-':
*modifier = (ca == '-' ? -1 : 1) * atoi(a + 1);
result = 1;
break;
case '\0':
result = 1;
break;
default:
result = 0;
break;
}
return result;
}
static long
params_to_pixels(TScreen * screen, String * params, Cardinal n)
{
int mult = 1;
const char *s;
int modifier;
switch (n > 2 ? 2 : n) {
case 2:
s = params[1];
if (CompareWidths(s, "PAGE", &modifier)) {
mult = (MaxRows(screen) + modifier) * FontHeight(screen);
} else if (CompareWidths(s, "HALFPAGE", &modifier)) {
mult = ((MaxRows(screen) + modifier) * FontHeight(screen)) / 2;
} else if (CompareWidths(s, "PIXEL", &modifier)) {
mult = 1;
} else {
mult = FontHeight(screen);
}
mult *= atoi(params[0]);
break;
case 1:
mult = atoi(params[0]) * FontHeight(screen);
break;
default:
mult = screen->scrolllines * FontHeight(screen);
break;
}
return mult;
}
static long
AmountToScroll(Widget w, String * params, Cardinal nparams)
{
long result = 0;
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
TScreen *screen = TScreenOf(xw);
if (nparams <= 2
|| screen->send_mouse_pos == MOUSE_OFF) {
result = params_to_pixels(screen, params, nparams);
}
}
return result;
}
void
HandleScrollForward(
Widget xw,
XEvent * event GCC_UNUSED,
String * params,
Cardinal *nparams)
{
long amount;
if ((amount = AmountToScroll(xw, params, *nparams)) != 0) {
ScrollTextUpDownBy(xw, (XtPointer) 0, (XtPointer) amount);
}
}
void
HandleScrollBack(
Widget xw,
XEvent * event GCC_UNUSED,
String * params,
Cardinal *nparams)
{
long amount;
if ((amount = -AmountToScroll(xw, params, *nparams)) != 0) {
ScrollTextUpDownBy(xw, (XtPointer) 0, (XtPointer) amount);
}
}
#if OPT_SCROLL_LOCK
#define SCROLL_LOCK_LED 3
#ifdef HAVE_XKBQUERYEXTENSION
static int
have_xkb(Display * dpy)
{
static int initialized = -1;
if (initialized < 0) {
int xkbmajor = XkbMajorVersion;
int xkbminor = XkbMinorVersion;
int xkbopcode, xkbevent, xkberror;
initialized = 0;
if (XkbLibraryVersion(&xkbmajor, &xkbminor)
&& XkbQueryExtension(dpy,
&xkbopcode,
&xkbevent,
&xkberror,
&xkbmajor,
&xkbminor)) {
TRACE(("we have Xkb\n"));
initialized = 1;
#if OPT_TRACE
{
XkbDescPtr xkb;
unsigned int mask;
int n;
char *modStr;
xkb = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
if (xkb != NULL) {
TRACE(("XkbGetKeyboard ok\n"));
for (n = 0; n < XkbNumVirtualMods; ++n) {
if (xkb->names->vmods[n] != 0) {
modStr = XGetAtomName(xkb->dpy,
xkb->names->vmods[n]);
if (modStr != 0) {
XkbVirtualModsToReal(xkb,
(unsigned) (1 << n),
&mask);
TRACE((" name[%d] %s (%#x)\n", n, modStr, mask));
}
}
}
XkbFreeKeyboard(xkb, 0, True);
}
}
#endif
}
}
return initialized;
}
static Boolean
getXkbLED(Display * dpy, const char *name, Boolean * result)
{
Atom my_atom;
Boolean success = False;
Bool state;
if (have_xkb(dpy)) {
my_atom = XInternAtom(dpy, name, False);
if ((my_atom != None) &&
XkbGetNamedIndicator(dpy, my_atom, NULL, &state, NULL, NULL)) {
*result = (Boolean) state;
success = True;
}
}
return success;
}
static Boolean
showXkbLED(Display * dpy, const char *name, Bool enable)
{
Atom my_atom;
Boolean result = False;
if (have_xkb(dpy)) {
my_atom = XInternAtom(dpy, name, False);
if ((my_atom != None) &&
XkbGetNamedIndicator(dpy, my_atom, NULL, NULL, NULL, NULL) &&
XkbSetNamedIndicator(dpy, my_atom, True, enable, False, NULL)) {
result = True;
}
}
return result;
}
#endif
static const char *led_table[] =
{
"Num Lock",
"Caps Lock",
"Scroll Lock"
};
static Boolean
xtermGetLED(TScreen * screen, Cardinal led_number)
{
Display *dpy = screen->display;
Boolean result = False;
#ifdef HAVE_XKBQUERYEXTENSION
if (!getXkbLED(dpy, led_table[led_number - 1], &result))
#endif
{
XKeyboardState state;
unsigned long my_bit = (unsigned long) (1 << (led_number - 1));
XGetKeyboardControl(dpy, &state);
result = (Boolean) ((state.led_mask & my_bit) != 0);
}
TRACE(("xtermGetLED %d:%s\n", led_number, BtoS(result)));
return result;
}
void
xtermShowLED(TScreen * screen, Cardinal led_number, Bool enable)
{
TRACE(("xtermShowLED %d:%s\n", led_number, BtoS(enable)));
if ((led_number >= 1) && (led_number <= XtNumber(led_table))) {
Display *dpy = screen->display;
#ifdef HAVE_XKBQUERYEXTENSION
if (!showXkbLED(dpy, led_table[led_number - 1], enable))
#endif
{
XKeyboardState state;
XKeyboardControl values;
unsigned long use_mask;
unsigned long my_bit = (unsigned long) (1 << (led_number - 1));
XGetKeyboardControl(dpy, &state);
use_mask = state.led_mask;
if (enable) {
use_mask |= my_bit;
} else {
use_mask &= ~my_bit;
}
if (state.led_mask != use_mask) {
values.led = (int) led_number;
values.led_mode = enable;
XChangeKeyboardControl(dpy, KBLed | KBLedMode, &values);
}
}
}
}
void
xtermClearLEDs(TScreen * screen)
{
Display *dpy = screen->display;
XKeyboardControl values;
TRACE(("xtermClearLEDs\n"));
#ifdef HAVE_XKBQUERYEXTENSION
ShowScrollLock(screen, False);
#endif
memset(&values, 0, sizeof(values));
XChangeKeyboardControl(dpy, KBLedMode, &values);
}
void
ShowScrollLock(TScreen * screen, Bool enable)
{
xtermShowLED(screen, SCROLL_LOCK_LED, enable);
}
void
GetScrollLock(TScreen * screen)
{
if (screen->allowScrollLock)
screen->scroll_lock = xtermGetLED(screen, SCROLL_LOCK_LED);
}
void
SetScrollLock(TScreen * screen, Bool enable)
{
if (screen->allowScrollLock) {
if (screen->scroll_lock != enable) {
screen->scroll_lock = (Boolean) enable;
ShowScrollLock(screen, enable);
}
}
}
void
HandleScrollLock(Widget w,
XEvent * event GCC_UNUSED,
String * params,
Cardinal *param_count)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
TScreen *screen = TScreenOf(xw);
if (screen->allowScrollLock) {
if (*param_count == 0) {
SetScrollLock(screen, !screen->scroll_lock);
TRACE(("HandleScrollLock ->%d\n",
screen->scroll_lock));
} else {
SetScrollLock(screen, atoi(params[0]));
TRACE(("HandleScrollLock(%s) ->%d\n",
params[0],
screen->scroll_lock));
}
}
}
}
#endif