_________ ___ d a w u / p n | \ 5 >> 9 >> q << g h / q o_____m l | g << _ b 8 e l | r / | | ____ __| |__ _ __ ___ ____ _ _ __ | s | | k | / __ /__ __| |/_ \ / _ \/__ || |/ \_/ \ 6 | t \ ______| j| \_ \ | | | / \_> __/ __| || /\ /\ | \ u y | _\ \ | |_ | | | |__ / _| || | | | | | m << \______v___x i z| /____ / \_/ |_| \___>\____||_| |_| |_| ___ | | v | a \______ / h | h y 3 v \ b d f / v. 1.6 c << t b \ __c_______g/ 1 >> a Simplified stream output/input for Allegro By Ole Laursen
See the README for information about how to install gstream and link your
program with it. The file NEWS contains a list of changes.
Welcome to gstream!
gstream is a C++ add-on library for Allegro. Its main purpose is to provide a simplified syntax for Allegro's keyboard and text functions for input and output, so that you can treat a graphical mode as a console, but there is more!
Among the most interesting features are:
Gstream is derived from the standard input/output stream classes and does consequently allow the well-known syntax from normal console putting, e.g.:
gs << "Hi there! Isn't this easy?\n"; int a; gs >> a; gs << " a is " << a;Or if you don't like the << and the >> operators:
gs.form("Hi there! This is easy, too.\n"); gs.form("The function outputs using the printf-style format strings,\n" "for example, a is %d", a);This is the basic idea behind gstream - to provide a very simple syntax for your outputting and inputting.
And it is really a relief when debugging because one doesn't have to think so much about where to send the debug information. You just blast it to the nearest gstream, like
gs << "AAAARGH, THE COMPUTER WILL BLOW UP IN 5 SECONDS BECAUSE a = " << a;
As stated in the introduction, the purpose of this library is to provide a simple syntax for in- and outputting. This is accomplished by deriving the gstream-class system from the standard stream classes, which means that all the functions that you (may) have written for the ostream/istream/ios classes will work with gstreams. It also means that everything you can do with the standard streams, e.g. formatting of floats, is also available with the gstreams.
As a consequence of this I haven't documented all of the functionality of the gstreams, only those parts which aren't shared with the standard streams. This means, for instance, that I don't write examples with the 'form' member function which can be used instead of (or as a supplement to) the << operator - if you prefer the printf-style you should seek information about it elsewhere from (e.g. the iostream description in the online help or a C++ book).
But let us start with the beginning of everything: the constructor. Its
declaration looks like this
gstream(BITMAP *bmp = screen);So to create a new gstream we pass the constructor a pointer to the BITMAP that we want the stream to draw to, e.g.
BITMAP *screen_buffer = create_bitmap(640, 480); gstream my_stream(screen_buffer), screen_strm;Here the second gstream, screen_strm, is initialized to draw to the screen by default. By the way, always remember to #include <gstream> before you try to use any of the gstream-functions or else Bad Things will happen (mmwhoah, hah, ha, ha, haaaaa...) such as the compiler giving you strange errors.
Now that we have learned how to create the stream, let us see how it is used. A typical and very common example is
#include <gstream> // note: you can use <gstream.h> if you prefer int main() { allegro_init(); // you have to initialise Allegro and in be in a if (set_gfx_mode(GFX_AUTODETECT, 320, 200, 0, 0) < 0) // graphical mode return 1; gstream gs; // construct gs, which will draw on the screen gs << "Hello world!"; // print string with default settings gs << flush; // gstreams are buffered so remember to flush return 0; }which will print the string "Hello world!" on the screen in the upper left corner. Of course you won't be able to see the string because the program will quickly terminate and return to the OS, but it should show you the basics: 'gs' works exactly like the standard 'cout'. Let us see an example of inputting:
#include <gstream> int main() { allegro_init(); if (set_gfx_mode(GFX_AUTODETECT, 320, 200, 0, 0) < 0) return 1; install_keyboard(); // when you use the inputter, you also have to // initialise the keyboard gstream gs; // once again construct gs gs << "Try inputting a number: "; int number; // define a number gs >> number; // get it gs << "\nYou wrote the number: " << number; // print it gs << flush; // remember to flush readkey(); // wait for a key to be pressed return 0; }And we notice that the gstream 'gs' also works like 'cin'. This is because a gstream serves both as an input and output stream. Now let's see what we have gathered of information:
If you have understood that getting and putting from and to a gstream is exactly like getting and putting from and to the standard streams, then you know how to use this library. The rest of it is just a bunch of routines to control the outputting and inputting and to make life a bit easier...
The term "cursor" should in this connection be thought of as the place where the next (if any) character will be drawn, and it exists simply as an invisible, imaginary point. When we talk about the "cursor" in connection with inputting, it is often manifested with for instance a vertical line that shows us where the next character will be inserted, but when generally speaking of the cursor and its position, it is just a point without any graphical representation.
The cursor is initialised to (0,0).
All the goto functions are also available as move_* in which case they move the cursor relative to the current position, e.g. if cursor position is (12, 40), then after move_xy(50,-10) it is (62, 30).
See also: goto_x, goto_y, move_xy.
See also: goto_xy, goto_y, move_x.
See also: goto_xy, goto_x, move_y.
y-coordinate = line_spacing * row
See also: move_row, goto_xy.
See also: goto_xy.
See also: goto_x.
See also: goto_y.
See also: goto_row.
This group of functions determines how the gstream will draw the data.
See also: get_bitmap.
See also: get_font.
See also: get_color.
See also: get_tab_size.
See also: get_margin.
Every time you select a new font, the gstream will call this function with 1.
See also: get_line_spacing.
If a single word is too wide, it'll simply be outputted with a newline before and after it, which means that it will get a line of its own and probably be truncated by Allegro's clipping mechanism.
The inputter doesn't wrap the lines, only the outputter. The default value is false.
These functions are only interesting if you need to get data from the gstream, they provide more control over the inputter.
char buf[100]; gs << "What's your opinion about gstream?\n"; gs.set_input_string("I think it is incredible wonderful!"); gs.getline(buf, 100);The user will see the following
What's your opinion about gstream? I think it is incredible wonderful!|where the '|' represents the cursor, and (s)he can edit the string, for example
What's your opinion about gstream? I think it is superior, fantastic and| wonderful!resulting in 'buf' containing the string "I think it is superior, fantastic and wonderful!"
See also: get_insert_mode.
set_max_input_length(5);and the user has made the following input
abcde|he won't be able to enter anymore characters before at least one of them has been deleted. If the three middle characters are deleted
a|ethen it is possible to enter three new characters wherever the user want. For example
1a2e3|Note that it is possible to set a default string which is longer than this supplied value; in that case the user will have to delete some characters if he wants to enter any himself.
Also note that this function only affects the next input, not the inputs after that.
See also: set_input_string.
The parameters of the drawer function are: destination bitmap (the BITMAP you draw on); x-coordinate; y-coordinate; the width (if you passed gbuf::DYNAMIC_CURSOR_SIZE it is the width of the character under the cursor, else it is the width you specified); the height (ditto); the colour of the font (watch out for a -1!); whether in insert mode (true if so).
A simple example of a standard console cursor:
void cd_cons(BITMAP *bmp, int x, int y, int w, int h, int c, bool ins) { int colour; // determine colour (defaults to black if no colour specified) if (c == -1) colour = makecol_depth(bitmap_color_depth(bmp), 0, 0, 0); else colour = c; if (ins) // if it is the insert cursor rectfill(bmp, x, y + h - 2, x + w - 1, y + h - 1, colour); else // else overwrite cursor rectfill(bmp, x, y, x + w - 1, y + h - 1, colour); }You don't have to think about flashing the cursor or other pecularities - the gstream will take care of that, all you have to do is to construct a function that draws something on the specified BITMAP, at the specified position, not exceeding the specified width and height. You are of course free to ignore the color and the insert parameter, just make sure that you don't draw outside the given rectangle, or else the gstream can't recover the background and the result will be artifacts.
A typedef, 'cursor_drawer', in 'gbuf' makes it easy to declare pointers to this type of functions:
gbuf::cursor_drawer tmp_cd; //... tmp_cd = gs1.get_cursor_drawer(); gs1.set_cursor_drawer(gs2.get_cursor_drawer()); gs2.set_cursor_drawer(tmp_cd);The default cursor drawer is 'cd_winconsole'.
See also: set_cursor_dimensions, get_cursor_drawer, cd_winconsole.
Before requesting any inputs with your custom cursor drawer, you should call this function with the appropriate values. To set the size of the character underneath the cursor dynamically, pass gbuf::DYNAMIC_CURSOR_SIZE: if the cursor is at the end of the line, it will get the width of a space ' ', else it will get the size of the character under it.
Say that we have created a nice cursor drawer that for the insert cursor is one pixel wide and as tall as the characters (in other words: a vertical line), but for the overwrite cursor is a BITMAP with the dimensions 8x10. Then the following would do:
gs.set_cursor_dimensions(1, gbuf::DYNAMIC_CURSOR_SIZE, 8, 10);It is important that you remember to call this function when you are using custom cursor drawers - else the inputter doesn't know how much of the background below the cursor to save and recover.
The default dimensions are the dimensions that are suited for the default cursor.
See also: get_cursor_w, get_cursor_h, set_cursor_drawer.
A short example:
bool ia_abc_is_what_we_want(int chr) { if (chr == 'a' || chr == 'b' || chr == 'c') return true; else return false; } //... gs.set_input_approver(ia_abc_is_what_we_want); gs >> my_string; // the user is only able to input a, b or cIf you want to declare any variables of the type of the function, the typedef 'input_approver' in 'gbuf' makes it easy, e.g.
gbuf::input_approver tmp_ap; //... tmp_ap = gs1.get_input_approver(); gs1.set_input_approver(gs2.get_input_approver()); gs2.set_input_approver(tmp_ap);Note that a default input string as entered by set_input_string is not checked by this function. The default approver is 'ia_allow_everything'.
See also: save_input_approver, restore_input_approver, get_input_approver, set_input_string, ia_allow_everything.
Saving will backup the currently used approver, so you can continue using it.
gs.save_input_approver(); gs >> my_string; // uses still default approver gs.set_input_approver(ia_my_own); gs >> my_string; // uses ia_my_own approver gs.restore_input_approver(); gs >> my_string; // uses default approver again
See also: restore_input_approver, set_input_approver, get_input_approver.
See also: save_input_approver, set_input_approver, get_input_approver.
Typical good values range from 50-80, but that is a matter of taste, of course.
Note that cursor blinking requires the timers to be installed as it uses a timer interrupt for timing the blinks. Default is off (0).
int stupid_mistakes = 0; void ieh_for_my_number_inputter(int e) { switch (e) { case gbuf::IE_UNRECOGNIZED_KEY: // note: gbuf:: if (stupid_mistakes > MUST_BE_BRAIN_DEAD) { say_to_user("Sorry, you can only input numbers!"); user_characteristics = FOOL; stupid_mistakes = 0; } else ++stupid_mistakes; break; case gbuf::IE_NO_ROOM_FOR_CHAR: play_quiet_beep(); break; } // in all other situations: do nothing }You should be aware that the inputter automatically will take care of any problems encountered and solve them with much elegance (often simply by ignoring the user if the result of the action would be illegal), so your error handler doesn't really deal with the problem or handle the situation - it is merely a way for you to warn or tell the user that he is doing something wrong.
Some advices concerning the style is given here - of course, you don't have to follow them, you are free to have you own opinion and the advices are general so they might not fit your particular situation, but please at least read them through before you begin designing your own error handler:
gbuf::input_error_handler tmp_ieh; //... tmp_ieh = gs1.get_input_error_handler(); gs1.set_input_error_handler(gs2.get_input_error_handler()); gs2.set_input_error_handler(tmp_ieh);The default input error handler is 'ieh_never_complain'.
See also: get_input_error_handler, set_input_approver, set_max_input_length, ieh_never_complain.
The gstreams support tabulator stops which can be a bit "smarter" than usual as they are capable of changing the font and/or the colour of the font on the fly.
Credits go to George Foot for this idea.
A few examples are granted:
set_tab(130); // set a tab stop at the x-position 130, don't change colour or font set_tab(400); // set a tab stop at 400, don't change colour or font set_tab(80, 24); // set a stop at 80 and let it change the colour to 24, no font change set_tab(190, 57, my_arial_font); // set tab stop at 190, change colour to 57 and font to 'my_arial_font' set_tab(250, courier); // set tab stop at 250, don't change colour, but change font to 'courier'
The first version of gstream didn't have these overloaded functions, but used a more clumsy syntax (involving a constant) which is now deprecated.
See also: remove_tab, remove_all_tabs, restore.
See also: set_tab, remove_all_tabs, restore.
See also: set_tab, remove_tab, restore.
It is automatically called when a newline ('\n') is encountered, and in a few other situations.
See also: set_tab, remove_tab, remove_all_tabs.
These functions return various of the gstream variables. Most of them speak pretty much for themselves, I think.
See also: set_bitmap.
See also: set_font.
See also: goto_x.
See also: goto_y.
See also: set_color.
See also: set_margin.
See also: set_tab_size.
See also: set_line_spacing.
See also: set_insert_mode.
See also: set_cursor_drawer.
See also: get_cursor_h, set_cursor_dimensions.
See also: get_cursor_w, set_cursor_dimensions.
See also: set_input_approver, save_input_approver, restore_input_approver.
See also: set_input_error_handler.
See also: set_dirty_rectangle_marker.
This group of functions all only have one thing in common: that they don't have anything in common.
The provided function will be given the parameters: x and y coordinates, and width and height of the rectangle, in that order. So if you're using DRS, you could call this function like this (since that function takes these four parameters x, y, width, height directly):
set_dirty_rectangle_marker(DRS_add_rectangle);The marker type function is typedef'ed in the class gbuf, which may come in handy in some situations:
gbuf::dirty_rectangle_marker tmp_drm; //... tmp_drm = gs1.get_dirty_rectangle_marker(); gs1.set_dirty_rectangle_marker(gs2.get_dirty_rectangle_marker()); gs2.set_dirty_rectangle_marker(tmp_drm);Pass the function a null pointer to prevent the gstream from thinking about dirty rectangles, e.g.
set_dirty_rectangle_marker(0); // or perhaps set_dirty_rectangle_marker(NULL);If you don't know what the "dirty" principle is all about: well, it is a screen-updating technique similar to double buffering but where you mark the areas that you have drawn on (which made them dirty), and then later when you need to update the screen only blit these areas (thereby cleaning the,) instead of the whole buffer. I suggest you get your hands on DRS if this has made you curious, since it contains an longer introduction to the principle (and also the routines to make it work, by the way :-). It can be fetched at
The default value of the dirty rectangle marker is the null pointer so that the gstream will not bother marking any rectangles.
Note that only the outputter will mark any rectangles dirty, and not the inputter since it is blocking program anyway, waiting for input, meanwhile preventing the main loop from reaching the place where it updates the rectangles.
See also: get_dirty_rectangle_marker.
fix a_number = 0.3; cout << a_number; cin >> a_number;in conjuction with the standard stream system. And, by the way, consequently also in conjuction with a gstream:
gstream gs; gs << a_number; gs >> a_number;You are probably not interested in calling them with their function names, only with the <</>> operators, so this paragraph is written just to make you aware of the possibility of doing that.
To make it easier to use the hook-installing functions in this library, there are some premade, ready-to-use functions bundled with the it. For example, if you want the user to be able to input numbers only, call set_input_approver with ia_allow_integer:
gs.save_input_approver(); gs.set_input_approver(ia_allow_integer); gs >> my_string; gs.restore_input_approver();The function collection is also a good place to start if you want to write a routine yourself. I suggest that you copy one of the functions and begin by modifying that - much nicer than starting from scratch. They are all located in the file gsfunc.cc, which also contains various comments.
See also: set_input_error_handler, ia_allow_decimal, ia_allow_hexadigits.
See also: set_input_error_handler, ia_allow_integer, ia_allow_hexadigits.
See also: set_input_error_handler, ia_allow_integer, ia_allow_decimal.
See also: set_input_error_handler.
See also: set_input_error_handler, ia_allow_word_chars.
See also: set_input_error_handler, ia_block_spaces.
The preferred dimensions for this cursor (you have to set them manually) are: (1, gbuf::DYNAMIC_CURSOR_SIZE, gbuf::DYNAMIC_CURSOR_SIZE, gbuf::DYNAMIC_CURSOR_SIZE) but the first argument can be bigger for a broader cursor.
See also: set_cursor_drawer, cd_console.
The preferred dimensions (you have to set them manually) are: (gbuf::DYNAMIC_CURSOR_SIZE, gbuf::DYNAMIC_CURSOR_SIZE, gbuf::DYNAMIC_CURSOR_SIZE, gbuf::DYNAMIC_CURSOR_SIZE) but the second argument can be set manually to control where the _ will be placed - low values and the cursor will fly above the letters, high values and the cursor will float at the bottom.
See also: set_cursor_drawer, cd_winconsole.
See also: set_input_error_handler.
You may want to copy it and adjust it to fit your particular situation.
See also: set_input_error_handler.
In case you don't know (or let us say: are not certain about :-) what a manipulator is, I'll give a short explanation. Don't worry if you don't understand the hairy technical details, they are not at all important unless you plan to write your own manipulators in which case you would probably have to read a proper description in an iostream documentation.
A manipulator is a function that can be called by using the extraction/insertion operators in conjuction with a stream. An example:
int number = 180; cout << number << " " << hex << number;This will give the output
180 0xB4because the part "<< hex" is actually a call to the function 'hex' that manipulates the stream to display the next number with hexadecimal digits. Another very common manipulator that you perhaps know is 'endl' which inserts a newline character '\n' into the stream and then flushes it:
cout << "This is the first line," << endl << "and this is the second.";Output:
This is the first line, and this is the second.The magical function calling is achieved partly by a template class system and partly by an operator>>/operator<< overload for i-/ostream. The overloaded operator functions take a pointer to a function; an example prototype is
ostream& operator<<(ostream& (*func)(ostream&));All the standard manipulators can of course be used with gstreams, but available with this library are also some that are specific to gstreams (and consequently only can be used with an instance of such). To use them you need to include the header "gmanip"
#include <gmanip>or as an alternative you can use the header <gmanip.h>.
The manipulators that take an argument are called with the argument as a normal function. For instance:
#include <gmanip> // ... gstream gs; gs << "At 0" << x(200) << "At 200"; // notice x(200)Note that another syntax is possible
x(gs,200) << "At 200";but it looks very weird (at least to me) and is harder to read so it somewhat defeats the purpose of using the manipulator in the first place. The "<< "At 200"" in the last example is, by the way, possible because the function 'x' returns a reference to the gstream.
A last thing to note about these manipulators before I begin describing them, is that they generally are somewhat slower than calling their equivalents in class gstream directly. This is in part due to that they cannot be inlined as the overloaded operators need pointers to functions. But do not totally ignore them because of this small speed penalty! I would consider replacing all
gs << manipulatorwith
gs.set_manipulatoras an optimization, and one should generally be careful with hand-made optimizations. They have their justification when speed really is essential, but else think twice before you spend a lot of your time making your code hard to read. My advice is that you use the set_* functions for maximum speed in places where it is appropriate (e.g. when you also have to draw a few dozens of other things on the screen in approximately a 1/70 of a second), but else use the manipulators for maximum readability.
Well, enough of this ranting. The manipulators of class gstream:
See also: goto_x.
See also: goto_y.
See also: goto_row.
See also: set_color.
See also: set_tab_size.
See also: set_margin.
See also: set_line_spacing.
See also: set_max_input_length.
See also: set_wrap.
See also: set_wrap.
See also: set_insert_mode.
See also: set_insert_mode.
The following macros can be used if you want to print information about the library.
See also: GSTREAM_DATE_STR, GSTREAM_VERSION, GSTREAM_SUB_VERSION.
(For version 19.43 it will probably be "3061" :-)
See also: GSTREAM_VERSION_STR, GSTREAM_VERSION, GSTREAM_SUB_VERSION.
See also: GSTREAM_VERSION_STR, GSTREAM_DATE_STR, GSTREAM_SUB_VERSION.
See also: GSTREAM_VERSION_STR, GSTREAM_DATE_STR, GSTREAM_VERSION.
For the legal aspects I will simply quote DRS:
This package is gift-ware. This package is given to you freely as a gift. You may use, modify, redistribute, and generally hack it about in any way you like, and you do not have to give anyone anything in return.
I do not accept any responsibility for any effects, adverse or otherwise, that this code may have on just about anything that you can think of. Use it at your own risk.
This package is written by Ole Laursen.
Since GStream 1.4 is maintainer of this library Michal Molhanec.
If you find any bugs or problems or things which in your opinion are wrong, please do not hesitate to tell me about them. The same applies to any other thoughts, comments or suggestions for improvements that you may have. Feedback encourages me to spend more time on gstream.
I can easily be contacted at
michal@molhanec.net
The latest version of this library should always be available on