! OKBGlulx - Library providing an convenient, object-oriented means of dealing with Glk windows. It also provides (what I consider) more convenient names for many glk calls.
! By Brendan "BrenBarn" Barnwell, alias OKB (
[email protected])
!
! The glk-call wrappers are entirely based on John "katre" Cater's infglk.h
!
! This library is, as its name implies, a Glulx-only library. It won't work with zcode Inform. In addition, OKBGlulx requires infglk.h -- it will Include it automatically if it is not already included.
!
! Please do contact me at
[email protected] with questions, comments, suggestions, etc.
!!!!!
! Initial-compatible
! OKBGlulx can make use of my Initial.h library. If you
System_file;
#IfNDef infglk_h;
Include "infglk";
#EndIf;
#IfDef Target_Glulx;
#IfNDef OKBGlulx;
Message "[Including <OKBGlulx>]";
!
! This is the Enterer object for use with my Initial library
!
#IfDef Enterer;
Enterer OKBGlulx;
EnterInitialise -> OKBGlulxInitialize
with
precedes Game,
execute [; rfalse; ]; ! No initialization needed
EnterIdentifyGlkObject -> OKBGlulxIGO
with follows Game,
execute [phase type objref objrock a;
objectloop (a ofclass Window) {
if (a~=MainWindow && a~=StatusWindow) a.winIdentify(phase,type,objref,objrock);
}
];
#IfNot;
Constant OKBGlulx;
#EndIf;
!
! This is the part that's just infglk with a face-lift
!
Constant StyleNormal 0;
Constant StyleEmphasized 1;
Constant StylePreformatted 2;
Constant StyleHeader 3;
Constant StyleSubheader 4;
Constant StyleAlert 5;
Constant StyleNote 6;
Constant StyleQuote 7;
Constant StyleInput 8;
Constant StyleUser1 9;
Constant StyleUser2 10;
Constant NumStyles 11;
Constant AllWindows 0;
Constant PairWin 1;
Constant BlankWin 2;
Constant BufferWin 3;
Constant GridWin 4;
Constant GraphicsWin 5;
Constant SplitLeft $00;
Constant SplitRight $01;
Constant SplitAbove $02;
Constant SplitBelow $03;
Constant SplitDirMask $0f;
Constant SplitFixed $10;
Constant SplitProportional $20;
Constant SplitDivisionMask $f0;
Constant HintIndent 0;
Constant HintParaIndent 1;
Constant HintJustify 2;
Constant HintSize 3;
Constant HintWeight 4;
Constant HintOblique 5;
Constant HintProportional 6;
Constant HintTextColor 7;
Constant HintBGColor 8;
Constant HintReverse 9;
Constant HintNumHints 10;
Constant JustifyLeft 0;
Constant JustifyFull 1;
Constant JustifyCenter 2;
Constant JustifyRight 3;
! Character functions
[GlkChar char;
return glk_put_char(char); ];
[GlkCharToUpper char;
return glk_char_to_upper(char); ];
[GlkCharToLower char;
return glk_char_to_lower(char); ];
! Commonly-used window functions
[GlkWinOpen split method size wintype rock;
return glk_window_open(split, method, size, wintype, rock); ];
[GlkWinClose win result;
return glk_window_close(win, result); ];
[GlkWinClear win;
return glk_window_clear(win); ];
[GlkWinBGColor win color;
return glk_window_set_background_color(win, color); ];
[GlkWinSet win;
return glk_set_window(win); ];
[GlkWinMoveCursor win x y;
return glk_window_move_cursor(win,x,y); ];
! Less-used window functions
[GlkWinRock win;
return glk_window_get_rock(win); ];
[GlkWinType win;
return glk_window_get_type(win); ];
[GlkWinParent win;
return glk_window_get_parent(win); ];
[GlkWinSibling win;
return glk_window_get_sibling(win); ];
[GlkWinWidth win;
glk_window_get_size(win,gg_arguments,gg_arguments+WORDSIZE);
return gg_arguments-->0; ];
[GlkWinHeight win;
glk_window_get_size(win,gg_arguments,gg_arguments+WORDSIZE);
return gg_arguments-->1; ];
! Styles
[GlkStyle styl;
return glk_set_style(styl); ];
[GlkGetStyle win style hint result;
if (result==0) {
glk_style_measure(win,style,hint,gg_arguments);
return (gg_arguments-->0);
}
glk_style_measure(win,style,hint,result);
return (result-->0);
];
[GlkStylehint wintype styl hint val;
return glk_stylehint_set(wintype, styl, hint, val); ];
[GlkUnStylehint wintype styl hint;
return glk_stylehint_clear(wintype, styl, hint); ];
!
! This part is extra routines I've added
!
[QuitWithError message value;
print (string) message;
print " The offending value is "; PrintAnything(value); print " (",value,").";
quit; ];
! Power: returns base^exponent
[Power base exponent;
if (exponent==0) return 1;
return base*(Power(base,exponent-1)); ];
#IfNDef Entry;
! IsInProp obj prop value
! object property something
! Returns: number
! Returns true if value is an entry in obj.prop, false if not.
[IsInProp obj prop value length i;
length=(obj.#prop)/WORDSIZE;
for (i=0 : i<length : i++) {
if ((obj.&prop-->i)==value) rtrue;
}
rfalse; ];
#EndIf;
!
! This part is the wrappers for window functions
!
! How to use this:
! For each window you want, just create an object ofclass Window, with all the properties defined as described below. You can then manipulate the windows using SomeWindow.open(), SomeWindow.close(), etc.
! When referring to other windows (most often when defining the existing window from which to split off a new window), use the object name (e.g., SomeWindow). Two premade objects, MainWindow and StatusWindow, have been provided to allow you to refer to (guess what) the main and status windows, respectively.
! Individual properties, which constitute the bulk of the windowing process, are described below.
Class Window
with
! Technical settings -- these define the mechanics of opening the window (size, type, etc.)
! These settings have no meaningful defaults. If you don't set them, something will go awry.
! EXCEPT for ref which you should NOT SET yourself -- that's the whole point of this library.
rock 0, ! Set this to some unique value
ref 0, ! Set to 0 if window doesn't currently exist -- DO NOT SET MANUALLY
type 0, ! BufferWin, GridWin, or GraphicsWin
splitfrom 0, ! The window from which this window will be split
! This should be the object ofclass Window which represents the window you want,
! not its rock or its ref. For example, if you create a Window object called FooWin
! and want to split BarWin off from FooWin, set BarWin.splitfrom=FooWin.
splitdirection 0, ! SplitAbove, SplitBelow, SplitLeft, or SplitRight
splittype 0, ! SplitFixed or SplitProportional
size 0, ! % of splitfrom if splittype==SplitProportional, or
! absolute size (in lines or columns) if splittype==SplitFixed
! Can be a routine, allowing you to reopen a window with a different size
! Height/width
! The height and width routines are provided so that you can get the height and width of an
! open window. The only safe use of these is to call them (as in SomeWindow.height(); -- don't
! try to set them.
width [; return GlkWinWidth(self.ref); ],
height [; return GlkWinHeight(self.ref); ],
! winRestore() -- For use after a restore, restart, or undo. After a restore, restart or undo, the
! library automatically recreates any windows that should be open. However, the contents of
! all windows will be lost. If you want to do special handling to restore a window to its former
! state, give your Window a winRestore routine that. (Don't call it yourself -- it'll call you. ! Actually, it will be automatically called in the event of a restore, restart, or undo.) When
! winRestore is called, the window is already open; all you have to do is print stuff to it, reload
! graphics, and in general make it LOOK like it did before. If you decide you don't want it open
! after all, you can close it.
winRestore [; rfalse; ],
! Stylehints -- these define what the window will look like (font, color, etc.)
! By default, all these settings will duplicate those of the story window, so a Window
! created without specifying any stylehints will be stylisitcally identical to the story window.
! These properties can be arrays containing stylehints for multiple styles, in this order:
! Normal->Emphasized->Preformatted->Header->Subheader->Alert->Note->BlockQuote->
! Input->User1->User2. For example, you can have a fontsize property containing the array
! [0 1 0 3 2]. The first value (0) will be hinted for Normal style, the second (1) for Emphasized,
! the third (0) for Preformatted, and so on. If you provide less array elements than there are
! styles (in other words, less than 11), an unspecified styles will not be hinted.
! In addition, these properties can be routines. This means you can have one window's ! style be dependent on another's (e.g., ThisWindow.style=[a; return OtherWindow.style(a); ];
! If any property is a routine, it must provide at least one local variable -- the library will call
! the routine with one argument: the current style being hinted. This means you can do stuff
! like:
! color [hinting;
! if (hinting==StyleNormal) return OtherWindow.color(hinting);
! if (hinting==StyleEmphasized) return YetAnotherWindow.color(hinting);
! . . .
! ],
indent ! Indentation from left margin (in spaces)
[a; return MainWindow.indent(a); ],
paragraph ! Indentation at beginning of each paragraph (in spaces)
[a; return MainWindow.paragraph(a); ],
justify ! Paragraph justification (JustifyLeft, JustifyCenter, JustifyRight, or JustifyFull)
[a; return MainWindow.justify(a); ],
fontsize ! Font size (0 means normal, positive means big, negative means small
[a; return MainWindow.fontsize(a); ],
weight ! 1 for bold, 0 for normal, -1 for light
[a; return MainWindow.weight(a); ],
oblique ! 1 for italics, 0 for normal
[a; return MainWindow.oblique(a); ],
proportional ! 1 for proportional, 0 for fixed-width
[a; return MainWindow.proportional(a); ],
color ! Routine to return text color, in hex
[a; return MainWindow.color(a); ],
bgcolor ! Routine to return background color, in hex
[a; return MainWindow.bgcolor(a); ],
reverse ! 1 for reverse color, 0 for normal
[a; return MainWindow.reverse(a); ],
! Note that color and bgcolor MUST be routines that return hex values -- they cannot contain
! the raw hex value. This is because Inform interprets hex numbers>$00FFFE as object
! numbers, and refuses to treat them as anything else.
! Routines -- Use these to perform window operations (open, close, etc.). You should only call
! these -- don't try to reassign their values.
! winOpen -- Opens the window. The specified stylehints will be set.
! Returns window ref, or -1 if window was already open, or -2 if the window it was supposed
! to split from doesn't exist.
winOpen
[; if (self.ref~=0) return -1;
if ( ((self.splitfrom).ref())==0 ) {
print "winOpen: splitfrom=",(name) self.splitfrom," / metaclass=",(name) metaclass(MainWindow.ref)," / MainWindow.ref=",MainWindow.ref(),"^";
return -2;
}
self.winStylehint(indent); self.winStylehint(paragraph); self.winStylehint(justify);
self.winStylehint(fontsize); self.winStylehint(weight); self.winStylehint(oblique);
self.winStylehint(proportional); self.winStylehint(color); self.winStylehint(bgcolor);
self.winStylehint(reverse);
self.ref=GlkWinOpen((self.splitfrom).ref(), (self.splitdirection+self.splittype), self.size(), self.type, self.rock);
! Notice that the window specified in splitfrom is the window OBJECT (i.e., an object ofclass Window), not the actual reference. OKBGlulx will find the reference on its own.
self.winUnStylehint(indent); self.winUnStylehint(paragraph); self.winUnStylehint(justify);
self.winUnStylehint(fontsize); self.winUnStylehint(weight); self.winUnStylehint(oblique);
self.winUnStylehint(proportional); self.winUnStylehint(color); self.winUnStylehint(bgcolor);
self.winUnStylehint(reverse);
return self.ref;
],
! winClose() -- Closes the window and clears the ref.
! Returns -1 if window was already closed, 0 otherwise
winClose
[; if (self.ref==0) return -1;
GlkWinClose(self.ref);
self.ref=0;
rfalse;
],
! winClear() -- Clears the window. Whee.
! Returns -1 if there is no window to clear (i.e., it is closed), 0 otherwise
winClear
[; if (self.ref==0) return -1;
GlkWinClear(self.ref); rfalse;
],
! winGoto() -- Switches focus to the window. Be sure to switch it back when you're done.
! Returns -1 if there is no window to switch to (i.e., it is closed), 0 otherwise
winGoto
[; if (self.ref==0) return -1;
GlkWinSet(self.ref); rfalse;
],
! winMoveCursor(x,y) -- Moves the cursor to position (x,y). Only valid for GridWin windows.
! Returns -1 if the window is closed, -2 if it is not a GridWin, 0 otherwise
winMoveCursor
[x y; if (self.ref==0) return -1;
if (self.type~=GridWin) return -2;
GlkWinMoveCursor(self.ref,x,y);
],
! winIdentify(phase, type, objref, objrock) -- You must call this routine from your
! IdentifyGlkObject routine, and you must pass it the same arguments that were passed to
! IdentifyGlkObject. You should never call this routine except from IdentifyGlkObject.
winIdentify
[phase type objref objrock;
if (phase==0) self.ref=0;
if (phase==1 && type==0) {
if (objrock==self.rock) self.ref=objref;
}
if (phase==2) {
if (self.ref) self.winRestore();
}
],
! Back-end properties -- These properties are used by the library, and are not intended to be
! referenced directly by the game author. You don't need to worry about them as long as you
! don't mess with them.
! winStylehint(hintprop) -- used when opening a window to set the stylehints.
! Returns -1 if hintprop was a routine, otherwise returns the number of hints set.
! hintprop is the current property being checked (e.g., fontsize, bgcolor). If a routine, it is
! executed, and the current style being hinted is passed as an argument. If an array, its elements
! are taken one by one, each being used to hint a different style.
! If future versions of Glulx add more stylehints, or change the existing ones, this routine
! will have to be modified to account for that.
winStylehint
[hintprop lastentry i;
for (i=0 : i<NumStyles : i++) {
if (metaclass(self.hintprop)==Routine) { ! If a routine, run it and set the
GlkStyleHint(self.type,i,HintName(hintprop),self.hintprop(i)); ! stylehint to the return value
}
else { ! It's an array
lastentry=((self.#hintprop)/WORDSIZE)-1; ! Have we run out of elements?
if (lastentry<i) return (i+1); ! If so, we're done
else GlkStylehint(self.type,i,HintName(hintprop),self.&hintprop-->i); ! If not, set the hint
}
}
],
! winUnStylehint(hintprop) -- resets all hints for a style after a window is opened.
! Returns true
winUnStylehint
[hintprop i;
for (i=0 : i<NumStyles : i++) GlkUnStylehint(self.type, i, HintName(hintprop));
];
! HintName(hintprop)
! This routine provides a link between the Window properties (indent, fontsize, etc.), and the corresponding stylehints (HintIndent, HintSize, etc.). Given one of the stylehint properties, it returns the corresponding stylehint constant.
[HintName hintprop;
switch (hintprop) {
indent: return HintIndent;
paragraph: return HintParaIndent;
justify: return HintJustify;
fontsize: return HintSize;
weight: return HintWeight;
oblique: return HintOblique;
proportional: return HintProportional;
color: return HintTextColor;
bgcolor: return HintBGColor;
reverse: return HintReverse;
default: QuitWithError("<a>OKBGlulx Error: HintProp given erroneous property name.</a>",hintprop);
}
];
! MainWindow and StatusWindow
! These objects are provided as a way for other window objects to obtain the rocks, references, styles, etc. of the story and status windows via the same OKBGlulx mechanisms used for user-defined windows.
! In the future, it might be worthwhile to have this library Replace InitGlkWindow and create the story and status windows on its own, in a manner consistent with the general modus operandi of the library. At this point, I'm content with functionality that simply makes it LOOK like they were created this way.
! To get the value of most of these settings, be sure to CALL the property (e.g., MainWindow.fontsize(), not just MainWindow.fontsize), because these are, after all, routines.
! Do not attempt to "do" anything to these windows (e.g., open them, close them) in your code. You can read the property values, but that's it.
Window MainWindow "[Main Window]"
with rock GG_MAINWIN_ROCK, ref [; return gg_mainwin; ], type BufferWin,
splitfrom [; QuitWithError("<a>[OKBGlulx Error: tried to find out what window gg_mainwin is split from.]</a>","MainWindow.splitfrom"); ],
splitdirection [; QuitWithError("<a>[OKBGlulx Error: tried to find out what direction gg_mainwin is split in.]</a>","MainWindow.splitdirection"); ],
splittype [; QuitWithError("<a>[OKBGlulx Error: tried to find the split type of gg_mainwin.]</a>","MainWindow.splittype"); ],
size [; QuitWithError("<a>OKBGlulx Error: tried to find the size of gg_mainwin.</a>","MainWindow.size"); ],
width [; return GlkWinWidth(gg_mainwin); ],
height [; return GlkWinHeight(gg_mainwin); ],
indent [hinting; return GlkGetStyle(gg_mainwin, hinting, HintIndent); ],
paragraph [hinting; return GlkGetStyle(gg_mainwin, hinting, HintParaIndent); ],
justify [hinting; return GlkGetStyle(gg_mainwin, hinting, HintJustify); ],
fontsize [hinting; return GlkGetStyle(gg_mainwin, hinting, HintSize); ],
weight [hinting; return GlkGetStyle(gg_mainwin, hinting, HintWeight); ],
oblique [hinting; return GlkGetStyle(gg_mainwin, hinting, HintOblique); ],
proportional [hinting; return GlkGetStyle(gg_mainwin, hinting, HintProportional); ],
color [hinting; return GlkGetStyle(gg_mainwin, hinting, HintTextColor); ],
bgcolor [hinting; return GlkGetStyle(gg_mainwin, hinting, HintBGColor); ],
reverse [hinting; return GlkGetStyle(gg_mainwin, hinting, HintReverse); ];
Window StatusWindow "[Statusline Window]"
with rock GG_STATUSWIN_ROCK, ref [; return gg_statuswin; ], type GridWin,
splitfrom [; return GlkWinParent(gg_statuswin); ],
splitdirection [; QuitWithError("<a>OKBGlulx Error: tried to find out what direction gg_statuswin is split in.]</a>","StatusWindow.splitdirection");],
splittype [; QuitWithError("<a>OKBGlulx Error: tried to find the split type of gg_statuswin.</a>","StatusWindow.splittype"); ],
size [; QuitWithError("<a>OKBGlulx Error: tried to find the size of gg_statuswin.</a>","StatusWindow.size"); ],
width [; return GlkWinWidth(gg_statuswin); ],
height [; return GlkWinHeight(gg_statuswin); ],
indent [hinting; return GlkGetStyle(gg_statuswin, hinting, HintIndent); ],
paragraph [hinting; return GlkGetStyle(gg_statuswin, hinting, HintParaIndent); ],
justify [hinting; return GlkGetStyle(gg_statuswin, hinting, HintJustify); ],
fontsize [hinting; return GlkGetStyle(gg_statuswin, hinting, HintSize); ],
weight [hinting; return GlkGetStyle(gg_statuswin, hinting, HintWeight); ],
oblique [hinting; return GlkGetStyle(gg_statuswin, hinting, HintOblique); ],
proportional [hinting; return GlkGetStyle(gg_statuswin, hinting, HintProportional); ],
color [hinting; return GlkGetStyle(gg_statuswin, hinting, HintTextColor); ],
bgcolor [hinting; return GlkGetStyle(gg_statuswin, hinting, HintBGColor); ],
reverse [hinting; return GlkGetStyle(gg_statuswin, hinting, HintReverse); ];
#EndIf;
#EndIf;