Is there a newline sequence that is supported for allegro's text output methods? It seems that \n dudn't work.
no
Dang, thanks anyways..
There isn't? That seems kinda not cool. Brings to mind the fact that Allegro makes cin unusable but offers no replacement...
Write a wrapper function for multiline text output. Give it the starting string including newlines , parse it into lines , use function parameters for the starting position , justification , vertical line spacing and text color for foreground/background and use the allegro text functions to display each line in the proper placement.
Well I don't see what std::cin has to do with text output. 
And std::cout still works, it outputs data to the console if one is available, otherwise the output is hidden. as with std::cin, it should read from the console, but if is a blocking function, so your game will pause while it gets input.
I am off to check that... -Tim
Edit: It definitely applies to linux, buggering off to windows to test mingw 
Edit2: It works fine under windows too.
Under both it spawns a console when you call std::cin.
It has nothing to do with text output, it's just something that's on my very short list of complaints about allegro. This thread lengthened that list to two things.
A way to implement Edgar Reynaldo's idea could be.
You could scan the string, when you find a newline, replace it with '\0', output the string, move your pointer to the character after the newline, move down 10 pixels (with the default font) and repeat til you reach the '\0'. Then run back along the string putting back all the newlines.
If you complain enough, someone will implement it for you, as its quite trivial. If I get bored later, I might even bother.
-Tim
I think Timorg's improvement would be faster and pretty easy too.
I think Timorg's improvement would be faster and pretty easy too.
There's absolutely no way you can justify not using the text_height function though.
So the multiline output is solved. Now as far as input goes you can use this.
Just a little warning on Timorg's version: It's extremely efficient but you had better document on your function that it modifies the string it received as input.
(If you pass static data that is normally in 'const' area, a touchy OS or a memory protector is allowed to complain. loudly.)
That depends whether you are doing those operations on the input string or you're copying it to some teporary string.
| 1 | #include <allegro.h> |
| 2 | |
| 3 | void textout_ml_ex(BITMAP *bmp, const FONT *f, const char *s, int x, int y, int color, int bg) |
| 4 | { |
| 5 | const char *start; |
| 6 | char *p, *line_start; |
| 7 | int cy; |
| 8 | |
| 9 | start = s; |
| 10 | p = (char *)s; |
| 11 | line_start = (char *)s; |
| 12 | cy = y; |
| 13 | |
| 14 | while (1) |
| 15 | { |
| 16 | if ((*p) == '\0') |
| 17 | break; |
| 18 | if ((*p) == '\n') |
| 19 | { |
| 20 | (*p) = '\0'; |
| 21 | textout_ex(bmp, f, line_start, x, cy, color, bg); |
| 22 | line_start = p + 1; |
| 23 | cy += text_height(f); |
| 24 | } |
| 25 | p++; |
| 26 | } |
| 27 | |
| 28 | textout_ex(bmp, f, line_start, x, cy, color, bg); |
| 29 | |
| 30 | while(p != start) |
| 31 | { |
| 32 | p--; |
| 33 | if ((*p) == '\0') |
| 34 | (*p) = '\n'; |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | int main() |
| 39 | { |
| 40 | allegro_init(); |
| 41 | install_keyboard(); |
| 42 | set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0); |
| 43 | |
| 44 | |
| 45 | char str[] = "This is a big long string\nit has quite a few lines of text\nhere is one\nhere is another"; |
| 46 | |
| 47 | textout_ex(screen, font, str, 0, 0, makecol(255, 255, 255), -1); |
| 48 | textout_ml_ex(screen, font, str, 0, 20, makecol(255, 255, 255), -1); |
| 49 | |
| 50 | readkey(); |
| 51 | set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); |
| 52 | |
| 53 | return 0; |
| 54 | } |
| 55 | END_OF_MAIN() |
I got bored, and as with what Audric was saying, this code isn't thread safe, if you try to use the string elsewhere at the same time, you will get strange things happening.
As for the other part of what Audric suggested, if your using STL strings, and call std::string.c_str() and pass it to the function, it depends on the implementation of STL to what will happen. Could be fine or yet again strange things might happen.
Thomas Harte: I didn't mention text_height function, cause I was trying to keep the example simple.
-------------
Edit:
I got bored again, here is a c++ implementation that uses std::string
| 1 | #include <allegro.h> |
| 2 | #include <string> |
| 3 | |
| 4 | void textout_ml_ex(BITMAP *bmp, const FONT *f, const std::string s, int x, int y, int color, int bg) |
| 5 | { |
| 6 | std::string leftovers = s; |
| 7 | int cy = y; |
| 8 | |
| 9 | while (1) |
| 10 | { |
| 11 | int count = leftovers.find("\n"); |
| 12 | if (count == -1) |
| 13 | break; |
| 14 | std::string current = leftovers.substr(0, count); |
| 15 | leftovers = leftovers.substr(count + 1); |
| 16 | textout_ex(bmp, f, current.c_str(), x, cy, color, bg); |
| 17 | cy += text_height(f); |
| 18 | } |
| 19 | textout_ex(bmp, f, leftovers.c_str(), x, cy, color, bg); |
| 20 | } |
| 21 | |
| 22 | int main() |
| 23 | { |
| 24 | allegro_init(); |
| 25 | install_keyboard(); |
| 26 | set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0); |
| 27 | |
| 28 | |
| 29 | std::string str("This is a big long string\nit has quite a few lines of text\nhere is one\nhere is another"); |
| 30 | |
| 31 | textout_ex(screen, font, str.c_str(), 0, 0, makecol(255, 255, 255), -1); |
| 32 | textout_ml_ex(screen, font, str, 0, 20, makecol(255, 255, 255), -1); |
| 33 | |
| 34 | readkey(); |
| 35 | set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); |
| 36 | |
| 37 | return 0; |
| 38 | } |
| 39 | END_OF_MAIN() |
This is thread safe, and you can pass a null terminated string into it and it will work fine.
I like the cleverness of the first solution anyway. My own hack would NOT have changed back the '\0' to '\n'
Thread-proofness is certainly never an issue here (drawing to bitmap = job for one thread), and as long as the string is writable, the memory managers/watchers have nothing to complain about.
I've been maintaining a codebase which had several..hmm..peculiarities like this, and as long as the prerequisites are listed at the function declaration, it's extremely reliable.
If you guys keep going, you'll come up with textprintf_ex() !
Except textprintf_ex doesn't handle newlines which is what prompted the whole debate.
You don't need to scan the string manually for newlines. strchr is your friend:
| 1 | int my_textout(BITMAP *bmp, FONT *font, const char *str, int x, int y, int fg, int bg) |
| 2 | { |
| 3 | char *str_copy = strdup(str); |
| 4 | char *cur, *next; |
| 5 | int i = 0; |
| 6 | |
| 7 | cur = str_copy; |
| 8 | while(cur) |
| 9 | { |
| 10 | next = strchr(cur, '\n'); |
| 11 | if(next) *next = 0; |
| 12 | i += textout_ex(bmp, font, cur, x, y, fg, bg); |
| 13 | if(next) *(next++) = '\n'; |
| 14 | y += text_height(font); |
| 15 | cur = next; |
| 16 | } |
| 17 | |
| 18 | free(str_copy); |
| 19 | } |
Now as far as input goes you can use this.
Yeah, but that seems a bit much just to let the player name his character...
After fixing the errors in Kitty Cat's code. ie. removing the redundant 'i' variable, and changing the return value to void.
I then made an optimization, where I removed one of the if statements.
I then made a version of my code where it duplicates the string, before messing with it, so that I wouldn't have to put all the '\n's in the right place.
I then stripped out the allegro stuff, and made a program that calls each function 10000000 times.
| 1 | #include <string.h> |
| 2 | #include <stdlib.h> |
| 3 | |
| 4 | void textout_ml_ex0(const char *s, int x, int y) |
| 5 | { |
| 6 | const char *start; |
| 7 | char *p, *line_start; |
| 8 | int cy; |
| 9 | |
| 10 | start = s; |
| 11 | p = (char *)s; |
| 12 | line_start = (char *)s; |
| 13 | cy = y; |
| 14 | |
| 15 | while (1) |
| 16 | { |
| 17 | if ((*p) == '\0') |
| 18 | break; |
| 19 | if ((*p) == '\n') |
| 20 | { |
| 21 | (*p) = '\0'; |
| 22 | line_start = p + 1; |
| 23 | } |
| 24 | p++; |
| 25 | } |
| 26 | |
| 27 | |
| 28 | while(p != start) |
| 29 | { |
| 30 | p--; |
| 31 | if ((*p) == '\0') |
| 32 | (*p) = '\n'; |
| 33 | } |
| 34 | } |
| 35 | |
| 36 | void textout_ml_ex1(const char *s, int x, int y) |
| 37 | { |
| 38 | const char *start; |
| 39 | char *p, *line_start; |
| 40 | int cy; |
| 41 | |
| 42 | |
| 43 | char *copy = strdup(s); |
| 44 | |
| 45 | start = copy; |
| 46 | p = (char *)copy; |
| 47 | line_start = (char *)copy; |
| 48 | cy = y; |
| 49 | |
| 50 | while (1) |
| 51 | { |
| 52 | if ((*p) == '\0') |
| 53 | break; |
| 54 | if ((*p) == '\n') |
| 55 | { |
| 56 | (*p) = '\0'; |
| 57 | line_start = p + 1; |
| 58 | } |
| 59 | p++; |
| 60 | } |
| 61 | |
| 62 | free(copy); |
| 63 | } |
| 64 | |
| 65 | void my_textout0(const char *str, int x, int y) |
| 66 | { |
| 67 | char *str_copy = strdup(str); |
| 68 | char *cur, *next; |
| 69 | |
| 70 | cur = str_copy; |
| 71 | while(cur) |
| 72 | { |
| 73 | next = strchr(cur, '\n'); |
| 74 | if(next) *next = 0; |
| 75 | if(next) *(next++) = '\n'; |
| 76 | cur = next; |
| 77 | } |
| 78 | |
| 79 | free(str_copy); |
| 80 | } |
| 81 | |
| 82 | void my_textout1(const char *str, int x, int y) |
| 83 | { |
| 84 | char *str_copy = strdup(str); |
| 85 | char *cur, *next; |
| 86 | |
| 87 | cur = str_copy; |
| 88 | while(cur) |
| 89 | { |
| 90 | next = strchr(cur, '\n'); |
| 91 | if (next) |
| 92 | { |
| 93 | *next = 0; |
| 94 | *(next++) = '\n'; |
| 95 | } |
| 96 | cur = next; |
| 97 | } |
| 98 | |
| 99 | free(str_copy); |
| 100 | } |
| 101 | |
| 102 | int main() |
| 103 | { |
| 104 | char str[] = "This is a big long string\nit has quite a few lines of text\nhere is one\nhere is another"; |
| 105 | int c; |
| 106 | |
| 107 | for (c = 0; c < 10000000; c++) |
| 108 | { |
| 109 | my_textout0(str, 0, 20); |
| 110 | } |
| 111 | |
| 112 | for (c = 0; c < 10000000; c++) |
| 113 | { |
| 114 | my_textout1(str, 0, 20); |
| 115 | } |
| 116 | |
| 117 | for (c = 0; c < 10000000; c++) |
| 118 | { |
| 119 | textout_ml_ex0(str, 0, 20); |
| 120 | } |
| 121 | |
| 122 | for (c = 0; c < 10000000; c++) |
| 123 | { |
| 124 | textout_ml_ex1(str, 0, 20); |
| 125 | } |
| 126 | |
| 127 | |
| 128 | return 0; |
| 129 | } |
I then profiled the program and got the following results.
% cumulative self self total time seconds seconds calls ns/call ns/call name 58.79 9.67 9.67 10000000 967.05 967.05 textout_ml_ex0 32.83 15.07 5.40 10000000 540.03 540.03 textout_ml_ex1 3.83 15.70 0.63 10000000 63.00 63.00 my_textout0 2.61 16.13 0.43 10000000 43.00 43.00 my_textout1 1.95 16.45 0.32 main
As you can see Kitty Cat's function is much more efficent than mine, its actual 15 times faster than my original code.
Then if you optimize it (-O1) the difference is even more pronounced
Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ns/call ns/call name 62.63 4.44 4.44 10000000 444.02 444.02 textout_ml_ex0 31.31 6.66 2.22 10000000 222.01 222.01 textout_ml_ex1 2.19 6.82 0.16 10000000 15.50 15.50 my_textout0 2.05 6.96 0.15 10000000 14.50 14.50 my_textout1 1.83 7.09 0.13 main
Which brings me to the final conclusion...
If you want multi-line text output use Kitty Cat's code (after you modify it so that it will actually compile without errors. 
-Tim
Yeah, but that seems a bit much just to let the player name his character...
With that code you can do pretty much everything you want. I have a working console using that code for input.
I was saying that if the only reason you wanted text input was to get the player's name, that would be a lot of code to do it with. It's worth it if you're actually using a lot of text input, but it's just too much if you only use it once, which is about how much I would use it in a game.
As far as I know there's no other simple way of doing this in Allegro.
I know, that's the problem. Allegro include functions to replace (and vastly improve upon) cout, but completely ignores the need for input. I'm going to use the mind control routines in Allegro 5 to make someone put input routines in Allegro 6.
The problem is with how the keyboard is managed. You can write your scanf/cin function that will hide that code above from you.
Yeah, I know it isn't that hard to do. But when I was first starting out I had no idea how to do it.
Reminds me of myself five years ago (that's approximately how that thread is old).
I have a question about the modified version of Kitty Cat's multi line text output. Specifically the line :
*(next++) = '\n';
From :
Since a copy is being worked on , the newline doesn't need to be replaced , just setting the address to the next character should suffice. What I don't understand is why the character after the detected newline should be set to a newline? Isn't "*(next++)" the same as "*(next + 1)"? Since 'cur' gets set to the character position right after where the newline is detected and that character position's value has been set to a newline , why doesn't strchr(cur , '\n') keep returning the same value as 'cur'?
In any case , I came up with another version , based on Kitty Cat's as well :
| 1 | |
| 2 | void multiline_textout_v1(BITMAP* bmp , FONT *font , const char *ml_str , int x , int y , int v_spacing , int fg , int bg) { |
| 3 | // note that this will only output the text lines of ml_str if each one ends with a newline '\n' character |
| 4 | // so "Line 1\nLine 2\n" would work , but not "Line 1\nLine 2" |
| 5 | // And so a normal string would not work either - ("Line 1") would fail |
| 6 | // due to the lack of a newline character |
| 7 | char* copy = strdup(ml_str); |
| 8 | |
| 9 | char* line = copy; |
| 10 | char* endline = strchr(line , '\n'); |
| 11 | |
| 12 | while(endline) { |
| 13 | *endline = '\0'; |
| 14 | textout_ex(bmp , font , line , x , y , fg , bg); |
| 15 | endline = strchr(line , '\n'); |
| 16 | line = endline + 1; |
| 17 | y += v_spacing; |
| 18 | } |
| 19 | |
| 20 | free(copy); |
| 21 | } |
Openlayer's TextRenderer does newlines automatically.
Since a copy is being worked on , the newline doesn't need to be replaced , just setting the address to the next character should suffice.
Correct. A mistake on my part.. 
What I don't understand is why the character after the detected newline should be set to a newline? Isn't "*(next++)" the same as "*(next + 1)"?
No. next++ post-increments the 'next' variable. It increments the variable, but "returns" the original value. ++next would increment the variable and give the new value.
A fixed version of my code would be:
| 1 | void my_textout(BITMAP *bmp, FONT *f, const char *str, int x, int y, int fg, int bg) |
| 2 | { |
| 3 | char *str_copy = strdup(str); |
| 4 | char *cur, *next; |
| 5 | |
| 6 | cur = str_copy; |
| 7 | while(cur) |
| 8 | { |
| 9 | next = strchr(cur, '\n'); |
| 10 | if(next) *(next++) = 0; |
| 11 | textout_ex(bmp, f, cur, x, y, fg, bg); |
| 12 | y += text_height(f); |
| 13 | cur = next; |
| 14 | } |
| 15 | |
| 16 | free(str_copy); |
| 17 | } |
No. next++ post-increments the 'next' variable. It increments the variable, but "returns" the original value. ++next would increment the variable and give the new value.
Weird. Not what I would expect to happen given that it's in parentheses. I never thought of post-incrementation as returning the original value.
Had to fix a bug in the version I put up and I also added in a function pointer parameter for which text function gets called. So now you can call textout_ex , textout_centre_ex , or textout_right_ex as an input parameter. My version now also displays the trailing end of the string containing newlines so you don't have to end it with a newline character. Use the v_spacing parameter to control the vertical spacing , and enter one of the three aforementioned allegro text functions for the text_func parameter.
Revised code :
| 1 | |
| 2 | void multiline_textout_v1a(BITMAP* bmp , FONT *font , const char *ml_str , int x , int y , int v_spacing , int fg , int bg , |
| 3 | void (*text_func) (BITMAP* , const FONT* , const char* , int , int , int , int)) { |
| 4 | |
| 5 | char* copy = strdup(ml_str); |
| 6 | |
| 7 | char* line = copy; |
| 8 | char* endline = strchr(line , '\n'); |
| 9 | |
| 10 | while(endline) { |
| 11 | *endline = '\0'; |
| 12 | text_func(bmp , font , line , x , y , fg , bg); |
| 13 | line = endline + 1; |
| 14 | endline = strchr(line , '\n'); |
| 15 | y += v_spacing; |
| 16 | } |
| 17 | endline = strchr(line , '\0'); |
| 18 | if (line != endline) { |
| 19 | text_func(bmp , font , line , x , y , fg , bg); |
| 20 | } |
| 21 | |
| 22 | free(copy); |
| 23 | } |