Files
mercury/extras/curses/mcurses.user.m
Zoltan Somogyi 9b6390b17e Bring the programming style of extras up to date.
extras/curs/curs.m:
extras/curs/curs.panel.m:
    Make panel a separate submodule of curs, not a nested submodule.

extras/base64/base64.m:
extras/curses/mcurses.basics.m:
extras/curses/mcurses.m:
extras/curses/mcurses.misc.m:
extras/curses/mcurses.user.m:
extras/gator/evolve.m:
extras/gator/genotype.m:
extras/gator/phenotype.m:
extras/gator/tausworthe3.m:
extras/monte/dots.m:
extras/monte/geom.m:
extras/monte/hg.m:
extras/monte/monte.m:
extras/monte/rnd.m:
extras/moose/grammar.m:
extras/moose/moose.m:
extras/mopenssl/mopenssl.m:
extras/net/echo.m:
extras/net/errno.m:
extras/net/getaddrinfo.m:
extras/net/net.m:
extras/net/netdb.m:
extras/net/sockets.m:
extras/net/streams.m:
extras/net/tcp.m:
extras/net/test_lookups.m:
extras/net/types.m:
extras/odbc/odbc.m:
extras/odbc/odbc_test.m:
extras/references/README:
extras/references/reference.m:
extras/references/scoped_update.m:
extras/solver_types/library/any.m:
extras/solver_types/library/any_array.m:
extras/solver_types/library/any_assoc_list.m:
extras/solver_types/library/any_list.m:
extras/solver_types/library/any_map.m:
extras/solver_types/library/any_tree234.m:
extras/solver_types/library/any_util.m:
extras/trail/trail.m:
extras/trailed_update/samples/interpreter.m:
extras/trailed_update/samples/vqueens.m:
extras/trailed_update/tests/var_test.m:
extras/trailed_update/tr_array.m:
extras/trailed_update/tr_store.m:
extras/trailed_update/trailed_update.m:
extras/trailed_update/unsafe.m:
extras/trailed_update/var.m:
    Bring programming style up to date.
2023-03-30 21:48:10 +11:00

720 lines
21 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1994-2000, 2005-2006, 2011 The University of Melbourne.
% Copyright (C) 2014, 2021-2022 The Mercury team.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury Distribution.
%---------------------------------------------------------------------------%
%
% File: mcurses.user.m
% Main author: conway
% Maintained by: rejj
% Stability: Medium
%
% This module provides the user-level functionality for the (n)curses binding.
%
% Please note that this is still a partial binding; it does not provide
% complete curses functionality.
% Major things this binding implements:
% * Creation, destruction, clearing, raising, and lowering of arbitrary
% windows.
% * Scrolling.
% * Colour on a character by character basis.
%
%---------------------------------------------------------------------------%
:- module mcurses.user.
:- interface.
:- import_module char.
:- import_module io.
:- import_module list.
:- import_module pair.
%---------------------------------------------------------------------------%
% The ADT used to represent a curses window.
%
:- type win.
% Window options.
%
:- type wopt
---> border % Place a border around the window.
; title(string). % Give the window a title.
% Character attributes.
% These modify the way a character is drawn on the screen. See the curses
% documentation for a detailed description of each attribute.
%
:- type cattr
---> normal
; standout
; underline
; reverse
; blink
; dim
; bold
; protect
; invis
; altcharset
; chartext
; colour(colour).
% Colours available for use in displaying characters.
%
% XXX Currently, there is no functionality provided for the customisation
% of colours, only the 8 default colours from curses can be used.
%
:- type colour
---> black
; green
; red
; cyan
; white
; magenta
; blue
; yellow.
% The type used to represent a character with its attributes.
%
:- type chtype == pair(char, list(cattr)).
% init(Root, !IO):
%
% Initialise curses, giving back the root window.
% The initialisation procedures in this library turn off echoing, and
% enable character-at-a-time input.
%
:- pred init(win::out, io::di, io::uo) is det.
% Redraw the screen.
%
:- pred redraw(io::di, io::uo) is det.
% Refresh the screen.
%
:- pred refresh(io::di, io::uo) is det.
% create(Parent, Options, ParentX, ParentY, NumCols, NumRows, Child, !IO):
%
% Create a new window, which will be a child of the window Parent. It is
% created at position ParentX, ParentY in the parent window, and is of size
% NumCols, NumRows.
%
:- pred create(win::in, list(wopt)::in, int::in, int::in, int::in, int::in,
win::out, io::di, io::uo) is det.
% Destroy the specified window.
%
:- pred destroy(win::in, io::di, io::uo) is det.
% Hide the specified window.
%
:- pred hide(win::in, io::di, io::uo) is det.
% Show the (previously hidden) specified window.
%
:- pred show(win::in, io::di, io::uo) is det.
% Raise the specified window.
%
:- pred raise(win::in, io::di, io::uo) is det.
% Lower the specified window.
%
:- pred lower(win::in, io::di, io::uo) is det.
% Clear the specified window. Fills the window with spaces.
%
:- pred clear(win::in, io::di, io::uo) is det.
% place_char(Window, X, Y, (Char - Attributes), !IO):
% Place a character into Window at position X, Y.
%
:- pred place_char(win::in, int::in, int::in, chtype::in,
io::di, io::uo) is det.
% place_string(Window, X, Y, String, !IO):
%
% Place a string into Window at position X, Y.
%
% XXX Note that presently, character attributes are not supported for
% strings
%
:- pred place_string(win::in, int::in, int::in, string::in,
io::di, io::uo) is det.
% scroll(Window, Amount, !IO):
%
% Scroll Window upwards by Amount lines.
%
:- pred scroll(win::in, int::in, io::di, io::uo) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- pragma foreign_decl("C", "
#include <curses.h>
#include <term.h>
").
%---------------------------------------------------------------------------%
:- import_module mcurses.basics.
:- import_module mcurses.misc.
:- import_module array.
:- import_module int.
:- import_module require.
:- import_module solutions.
:- import_module store.
:- import_module string.
%---------------------------------------------------------------------------%
:- type curse_store_type
---> curse_store_type.
:- type curse_store == store(curse_store_type).
:- type win == store_mutvar(window, curse_store_type).
:- type window
---> win(
win, % parent
int, % width
int, % height
list(wopt),
array(chtype), % contents
list(child), % visible
list(child) % hidden
).
:- type child
---> child(
int, % x
int, % y
win
).
:- type cursor
---> cursor(int, int). % X, Y
%---------------------------------------------------------------------------%
init(Win, !IO) :-
init(!IO),
cols(Cols, !IO),
rows(Rows, !IO),
array.init(Cols * Rows, ' ' - [], Data),
MakeWin = (func(Self) = win(Self, Cols, Rows, [], Data, [], [])),
init_curse_store(Curse0),
store.new_cyclic_mutvar(MakeWin, Win, Curse0, Curse),
set_curse_store(Curse, !IO),
set_root(Win, !IO),
refresh(!IO).
%---------------------------------------------------------------------------%
create(Parent, Opts, X, Y, W, H, Child, !IO) :-
get_win(Parent, PWindow0, !IO),
PWindow0 = win(P0, W0, H0, Opts0, PData, Visi0, Hidden),
require(
((pred) is semidet :-
X >= 0, Y >= 0,
X+W =< W0,
Y+H =< H0
), "create: window out of range!"),
array.init(W * H, ' ' - [], Data),
CWindow = win(P0, W, H, Opts, Data, [], []),
new_win(CWindow, Child, !IO),
list.append(Visi0, [child(X, Y, Child)], Visi),
PWindow = win(Parent, W0, H0, Opts0, PData, Visi, Hidden),
set_win(Parent, PWindow, !IO).
%---------------------------------------------------------------------------%
destroy(Win, !IO) :-
get_win(Win, Window, !IO),
Window = win(Parent, _, _, _, _, _, _),
( if Parent \= Win then % Cannot kill the root window.
get_win(Parent, PWindow0, !IO),
PWindow0 = win(PP, PC, PR, PO, PD, Visi0, Hidden),
filter(
( pred(Child::in) is semidet :-
not Child = child(_, _, Win)
), Visi0, Visi),
PWindow = win(PP, PC, PR, PO, PD, Visi, Hidden),
set_win(Parent, PWindow, !IO)
else
true
).
%---------------------------------------------------------------------------%
redraw(!IO) :-
get_root(Root, !IO),
set_cursor(cursor(0, 0), !IO),
refresh(Root, !IO),
doupdate(!IO).
%---------------------------------------------------------------------------%
refresh(!IO) :-
get_root(Root, !IO),
clear(!IO),
set_cursor(cursor(0, 0), !IO),
refresh(Root, !IO),
update(!IO).
:- pred refresh(win::in, io::di, io::uo) is det.
refresh(Win, !IO) :-
get_win(Win, Window, !IO),
Window = win(_Parent, Cols, Rows, Opts, Data, Visi, _Hidden),
get_cursor(cursor(X0, Y0), !IO),
solutions(
( pred(Ti::out) is nondet :-
list.member(ZZ, Opts),
ZZ = title(Ti)
), Titles),
( if list.member(border, Opts) then
for(Y0+1, Y0+Rows,
( pred(By::in, !.IO::di, !:IO::uo) is det :-
cursor(X0, By, !IO),
putchar('|', !IO),
cursor(X0 + Cols + 1, By, !IO),
putchar('|', !IO)
), !IO),
for(X0 + 1, X0 + Cols,
( pred(Bx::in, !.IO::di, !:IO::uo) is det :-
cursor(Bx, Y0, !IO),
(
Titles = [],
putchar('-', !IO)
;
Titles = [_ | _],
putchar('=', !IO)
),
cursor(Bx, Y0 + Rows + 1, !IO),
putchar('-', !IO)
), !IO),
cursor(X0, Y0, !IO), putchar('+', !IO),
cursor(X0 + Cols + 1, Y0, !IO), putchar('+', !IO),
cursor(X0, Y0 + Rows + 1, !IO), putchar('+', !IO),
cursor(X0 + Cols + 1, Y0 + Rows + 1, !IO), putchar('+', !IO),
( if Titles = [Title0 | _] then
string.length(Title0, N0),
( if N0 > Cols - 2 then
N = Cols - 2,
split(Title0, N, Title, _)
else
N = N0,
Title = Title0
),
Xst = X0 + (Cols - N) // 2,
cursor(Xst, Y0, !IO),
putstr(Title, !IO)
else
true
),
A = 1
else
A = 0
),
Xb = X0 + A,
Yb = Y0 + A,
for(0, Rows - 1,
( pred(Y::in, !.IO::di, !:IO::uo) is det :-
Offset = Y*Cols,
for(0, Cols - 1,
( pred(X::in, !.IO::di, !:IO::uo) is det :-
cursor(Xb + X, Yb + Y, !IO),
lookup(Data, X + Offset, Char - Attribs),
putch(Char, Attribs, !IO)
), !IO)
), !IO),
foldl(refresh_child, Visi, !IO).
:- pred refresh_child(child::in, io::di, io::uo) is det.
refresh_child(child(X, Y, Win), !IO):-
get_cursor(cursor(X0, Y0), !IO),
set_cursor(cursor(X0 + X, Y0 + Y), !IO),
refresh(Win, !IO),
set_cursor(cursor(X0, Y0), !IO).
%---------------------------------------------------------------------------%
:- pred putch(char::in, list(cattr)::in, io::di, io::uo) is det.
putch(Char, [], !IO) :-
putchar(Char, !IO).
putch(Char, [A | B], !IO) :-
chtype(Char, Chtype),
putch2(Chtype, [A | B], !IO).
:- pred putch2(int::in, list(cattr)::in, io::di, io::uo) is det.
putch2(Chtype, [], !IO) :-
putch3(Chtype, !IO).
putch2(Chtype0, [Attrib | Attribs], !IO) :-
(
Attrib = normal,
mod_chtype(Chtype0, normal, Chtype),
putch2(Chtype, Attribs, !IO)
;
Attrib = standout,
mod_chtype(Chtype0, standout, Chtype),
putch2(Chtype, Attribs, !IO)
;
Attrib = underline,
mod_chtype(Chtype0, underline, Chtype),
putch2(Chtype, Attribs, !IO)
;
Attrib = reverse,
mod_chtype(Chtype0, reverse, Chtype),
putch2(Chtype, Attribs, !IO)
;
Attrib = blink,
mod_chtype(Chtype0, blink, Chtype),
putch2(Chtype, Attribs, !IO)
;
Attrib = dim,
mod_chtype(Chtype0, dim, Chtype),
putch2(Chtype, Attribs, !IO)
;
Attrib = bold,
mod_chtype(Chtype0, bold, Chtype),
putch2(Chtype, Attribs, !IO)
;
Attrib = protect,
mod_chtype(Chtype0, protect, Chtype),
putch2(Chtype, Attribs, !IO)
;
Attrib = invis,
mod_chtype(Chtype0, invis, Chtype),
putch2(Chtype, Attribs, !IO)
;
Attrib = altcharset,
mod_chtype(Chtype0, altcharset, Chtype),
putch2(Chtype, Attribs, !IO)
;
Attrib = chartext,
mod_chtype(Chtype0, chartext, Chtype),
putch2(Chtype, Attribs, !IO)
;
Attrib = colour(Colour0),
get_colour(Colour0, Colour),
mod_chtype(Chtype0, colour(Colour), Chtype),
putch2(Chtype, Attribs, !IO)
).
:- pred putch3(int::in, io::di, io::uo) is det.
:- pragma foreign_proc("C",
putch3(C::in, _IO0::di, _IO::uo),
[promise_pure, will_not_call_mercury, tabled_for_io],
"
addch((chtype) C);
").
%---------------------------------------------------------------------------%
:- pred get_colour(colour::in, int::out) is det.
get_colour(black, black).
get_colour(green, green).
get_colour(red, red).
get_colour(cyan, cyan).
get_colour(white, white).
get_colour(magenta, magenta).
get_colour(blue, blue).
get_colour(yellow, yellow).
:- pred chtype(char::in, int::out) is det.
:- pragma foreign_proc("C",
chtype(C::in, Ch::out),
[promise_pure, will_not_call_mercury],
"
Ch = (chtype) C;
").
:- pred mod_chtype(int::in, int::in, int::out) is det.
:- pragma foreign_proc("C",
mod_chtype(Ch0::in, Attr::in, Ch::out),
[promise_pure, will_not_call_mercury],
"
Ch = (chtype) Ch0 | Attr;
").
%---------------------------------------------------------------------------%
hide(Win, !IO) :-
get_win(Win, Window, !IO),
Window = win(Parent, _, _, _, _, _, _),
get_win(Parent, PWindow0, !IO),
PWindow0 = win(PP, PC, PR, PO, PD, Visi0, Hidden0),
filter((pred(Child::in) is semidet :-
Child = child(_, _, Win)
), Visi0, This, Visi),
append(This, Hidden0, Hidden),
PWindow = win(PP, PC, PR, PO, PD, Visi, Hidden),
set_win(Parent, PWindow, !IO).
%---------------------------------------------------------------------------%
show(Win, !IO) :-
get_win(Win, Window, !IO),
Window = win(Parent, _, _, _, _, _, _),
get_win(Parent, PWindow0, !IO),
PWindow0 = win(PP, PC, PR, PO, PD, Visi0, Hidden0),
filter((pred(Child::in) is semidet :-
Child = child(_, _, Win)
), Hidden0, This, Hidden),
append(Visi0, This, Visi),
PWindow = win(PP, PC, PR, PO, PD, Visi, Hidden),
set_win(Parent, PWindow, !IO).
%---------------------------------------------------------------------------%
raise(Win, !IO) :-
get_win(Win, Window, !IO),
Window = win(Parent, _, _, _, _, _, _),
get_win(Parent, PWindow0, !IO),
PWindow0 = win(PP, PC, PR, PO, PD, Visi0, Hidden),
filter((pred(Child::in) is semidet :-
Child = child(_, _, Win)
), Visi0, This, Rest),
append(Rest, This, Visi),
PWindow = win(PP, PC, PR, PO, PD, Visi, Hidden),
set_win(Parent, PWindow, !IO).
%---------------------------------------------------------------------------%
lower(Win, !IO) :-
get_win(Win, Window, !IO),
Window = win(Parent, _, _, _, _, _, _),
get_win(Parent, PWindow0, !IO),
PWindow0 = win(PP, PC, PR, PO, PD, Visi0, Hidden),
filter((pred(Child::in) is semidet :-
Child = child(_, _, Win)
), Visi0, This, Rest),
append(This, Rest, Visi),
PWindow = win(PP, PC, PR, PO, PD, Visi, Hidden),
set_win(Parent, PWindow, !IO).
%---------------------------------------------------------------------------%
clear(Win, !IO) :-
get_win(Win, win(Parent, Cols, Rows, Opts, Data0, Visi, Hidden), !IO),
for(0, Rows - 1,
( pred(Y::in, array_di, array_uo) is det -->
for(0, Cols - 1,
( pred(X::in, D0::array_di, D::array_uo) is det :-
set(X + Y * Cols, ' ' - [], D0, D)
))
), u(Data0), Data),
set_win(Win, win(Parent, Cols, Rows, Opts, Data, Visi, Hidden), !IO).
%---------------------------------------------------------------------------%
scroll(Win, N, !IO) :-
get_win(Win, win(Parent, Cols, Rows, Opts, Data0, Visi, Hidden), !IO),
require(((pred) is semidet :-
N > 0,
N < Cols
), "scroll: out of range"),
for(0, Rows - N - 1,
( pred(Y::in, array_di, array_uo) is det -->
for(0, Cols - 1,
( pred(X::in, D0::array_di, D::array_uo) is det :-
lookup(D0, X + (Y + N) * Cols, C),
set(X + Y * Cols, C, D0, D)
))
), u(Data0), Data1),
for(Rows - N, Rows - 1,
( pred(Y::in, array_di, array_uo) is det -->
for(0, Cols - 1,
( pred(X::in, D1::array_di, Q::array_uo) is det :-
set(X + Y * Cols, ' ' - [], D1, Q)
))
), Data1, Data),
set_win(Win, win(Parent, Cols, Rows, Opts, Data, Visi, Hidden), !IO).
%---------------------------------------------------------------------------%
place_char(Win, X, Y, C - As, !IO) :-
get_win(Win, win(Parent, Cols, Rows, Opts, Data0, Visi, Hidden), !IO),
require(
((pred) is semidet :-
X >= 0, Y >= 0,
X < Cols, Y < Cols
), "place_char: out of range"),
set(X + Y * Cols, C - As, u(Data0), Data),
set_win(Win, win(Parent, Cols, Rows, Opts, Data, Visi, Hidden), !IO).
:- func u(array(T)) = array(T).
:- mode (u(in) = array_uo) is det.
:- pragma foreign_proc("C",
u(A::in) = (B::array_uo),
[promise_pure, will_not_call_mercury],
"
B = A;
").
%---------------------------------------------------------------------------%
place_string(Win, X, Y, Str, !IO) :-
get_win(Win, win(Parent, Cols, Rows, Opts, Data0, Visi, Hidden), !IO),
require(
((pred) is semidet :-
X >= 0, Y >= 0,
X < Cols, Y < Cols
), "place_string: out of range"),
string.to_char_list(Str, Chars),
update_data(Chars, Y * Cols, X, X + Cols, u(Data0), Data),
set_win(Win, win(Parent, Cols, Rows, Opts, Data, Visi, Hidden), !IO).
:- pred update_data(list(char)::in, int::in, int::in, int::in,
array(pair(char, list(cattr)))::array_di,
array(pair(char, list(cattr)))::array_uo) is det.
update_data([], _, _, _, !Data).
update_data([C | Cs], Y, X, Xmax, !Data) :-
( if X < Xmax then
array.set(X + Y, C - [], !Data),
update_data(Cs, Y, X + 1, Xmax, !Data)
else
true
).
%---------------------------------------------------------------------------%
:- pragma foreign_decl("C", "
extern MR_Word curse_root;
").
:- pragma foreign_code("C", "
MR_Word curse_root;
").
:- pred get_root(win::out, io::di, io::uo) is det.
:- pragma foreign_proc("C",
get_root(W::out, _IO0::di, _IO::uo),
[promise_pure, will_not_call_mercury, tabled_for_io],
"
W = curse_root;
").
:- pred set_root(win::in, io::di, io::uo) is det.
:- pragma foreign_proc("C",
set_root(W::in, _IO0::di, _IO::uo),
[promise_pure, will_not_call_mercury, tabled_for_io],
"
curse_root = W;
").
%---------------------------------------------------------------------------%
:- pred new_win(window::in, win::out, io::di, io::uo) is det.
new_win(Window, Win, !IO) :-
get_curse_store(Curse0, !IO),
store.new_mutvar(Window, Win, Curse0, Curse),
set_curse_store(Curse, !IO).
:- pred get_win(win::in, window::out, io::di, io::uo) is det.
get_win(Win, Window, !IO) :-
get_curse_store(Curse0, !IO),
store.get_mutvar(Win, Window, Curse0, Curse),
set_curse_store(Curse, !IO).
:- pred set_win(win::in, window::in, io::di, io::uo) is det.
set_win(Win, Window, !IO) :-
get_curse_store(Curse0, !IO),
store.set_mutvar(Win, Window, Curse0, Curse),
set_curse_store(Curse, !IO).
%---------------------------------------------------------------------------%
:- pragma foreign_decl("C", "
extern MR_Word curse_cursor;
").
:- pragma foreign_code("C", "
MR_Word curse_cursor;
").
:- pred get_cursor(cursor::out, io::di, io::uo) is det.
:- pragma foreign_proc("C",
get_cursor(C::out, _IO0::di, _IO::uo),
[promise_pure, will_not_call_mercury, tabled_for_io],
"
C = curse_cursor;
").
:- pred set_cursor(cursor::in, io::di, io::uo) is det.
:- pragma foreign_proc("C",
set_cursor(C::in, _IO0::di, _IO::uo),
[promise_pure, will_not_call_mercury, tabled_for_io],
"
curse_cursor = C;
").
%---------------------------------------------------------------------------%
% XXX get_curse_store is not unique-mode-correct.
% You need to be careful to ensure that get_curse_store
% and set_curse_store are only ever used in pairs.
:- pragma foreign_decl("C", "
extern MR_Word curse_store;
").
:- pragma foreign_code("C", "
MR_Word curse_store;
").
:- pred init_curse_store(curse_store::uo) is det.
:- pragma foreign_proc("C",
init_curse_store(C::uo),
[promise_pure, will_not_call_mercury],
"
// Here we rely on the fact that stores have no real representation, so we
// can fill in any dummy value for C.
C = 0;
").
:- pred get_curse_store(curse_store::uo, io::di, io::uo) is det.
:- pragma foreign_proc("C",
get_curse_store(C::uo, _IO0::di, _IO::uo),
[promise_pure, will_not_call_mercury, tabled_for_io],
"
C = curse_store;
").
:- pred set_curse_store(curse_store::di, io::di, io::uo) is det.
:- pragma foreign_proc("C",
set_curse_store(C::di, _IO0::di, _IO::uo),
[promise_pure, will_not_call_mercury, tabled_for_io],
"
curse_store = C;
").
%---------------------------------------------------------------------------%
:- end_module mcurses.user.
%---------------------------------------------------------------------------%