I have a ncurses program which is a login menu and I use field for username and password.
The problem is that when I type something in the fields, characters register but do not show in terminal. In other word if you execute the code blow and type something, you won't be able to see it in terminal but if you push F2, you could see that the characters were registered.
Here is my code:
test.cpp
#include <curses.h>
#include <form.h>
#include <menu.h>
#include <string>
#include <cstring>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
class WelcomeMenu {
private:
int _row; // number of rows of the terminal
int _col; // number of columns of the terminal
public:
WelcomeMenu();
~WelcomeMenu();
void welcomeBox();
void loginMenu();
void registerMenu();
};
WelcomeMenu::WelcomeMenu(){
initscr();
noecho();
cbreak();
keypad(stdscr, true);
int row, col;
getmaxyx(stdscr,row,col); /* get the number of rows and columns */
this->_row = row; this->_col = col;
loginMenu();
}
WelcomeMenu::~WelcomeMenu(){
refresh();
endwin();
}
/*
* This is useful because ncurses fill fields blanks with spaces.
*/
char* trim_whitespaces(char *str)
{
char *end;
// trim leading space
while(isspace(*str))
str++;
if(*str == 0) // all spaces?
return str;
// trim trailing space
end = str + strnlen(str, 128) - 1;
while(end > str && isspace(*end))
end--;
// write new null terminator
*(end+1) = '\0';
return str;
}
void WelcomeMenu::loginMenu(){
// erase();
FORM *form;
FIELD *fields[5];
WINDOW *win_body, *win_form;
int ch;
win_body = newwin(24, 80, 0, 0);
assert(win_body != NULL);
box(win_body, 0, 0);
win_form = derwin(win_body, 20, 78, 3, 1);
assert(win_form != NULL);
box(win_form, 0, 0);
mvwprintw(win_body, 1, 2, "Press F1 to quit and F2 to print fields content");
fields[0] = new_field(1, 10, 0, 0, 0, 0);
fields[1] = new_field(1, 40, 0, 15, 0, 0);
fields[2] = new_field(1, 10, 2, 0, 0, 0);
fields[3] = new_field(1, 40, 2, 15, 0, 0);
fields[4] = NULL;
assert(fields[0] != NULL && fields[1] != NULL && fields[2] != NULL && fields[3] != NULL);
set_field_buffer(fields[0], 0, "Username: ");
set_field_buffer(fields[1], 0, "username");
set_field_buffer(fields[2], 0, "Password: ");
set_field_buffer(fields[3], 0, "password");
set_field_opts(fields[0], O_VISIBLE | O_PUBLIC | O_AUTOSKIP);
set_field_opts(fields[1], O_VISIBLE | O_PUBLIC | O_EDIT | O_ACTIVE);
set_field_opts(fields[2], O_VISIBLE | O_PUBLIC | O_AUTOSKIP);
set_field_opts(fields[3], O_VISIBLE | O_PUBLIC | O_EDIT | O_ACTIVE);
set_field_back(fields[1], A_UNDERLINE);
set_field_back(fields[3], A_UNDERLINE);
form = new_form(fields);
assert(form != NULL);
set_form_win(form, win_form);
set_form_sub(form, derwin(win_form, 18, 76, 1, 1));
post_form(form);
refresh();
wrefresh(win_body);
wrefresh(win_form);
while ((ch = getch()) != KEY_F(1)){
switch (ch) {
case KEY_F(2):
// Or the current field buffer won't be sync with what is displayed
form_driver(form, REQ_NEXT_FIELD);
form_driver(form, REQ_PREV_FIELD);
move(LINES-3, 2);
for (int i = 0; fields[i]; i++) {
printw("%s", trim_whitespaces(field_buffer(fields[i], 0)));
if (field_opts(fields[i]) & O_ACTIVE)
printw("\"\t");
else
printw(": \"");
}
refresh();
pos_form_cursor(form);
break;
case KEY_DOWN:
form_driver(form, REQ_NEXT_FIELD);
form_driver(form, REQ_END_LINE);
break;
case KEY_UP:
form_driver(form, REQ_PREV_FIELD);
form_driver(form, REQ_END_LINE);
break;
case KEY_LEFT:
form_driver(form, REQ_PREV_CHAR);
break;
case KEY_RIGHT:
form_driver(form, REQ_NEXT_CHAR);
break;
// Delete the char before cursor
case KEY_BACKSPACE:
case 127:
form_driver(form, REQ_DEL_PREV);
break;
// Delete the char under the cursor
case KEY_DC:
form_driver(form, REQ_DEL_CHAR);
break;
default:
form_driver(form, ch);
break;
}
}
wrefresh(win_form);
unpost_form(form);
free_form(form);
free_field(fields[0]);
free_field(fields[1]);
free_field(fields[2]);
free_field(fields[3]);
delwin(win_form);
delwin(win_body);
}
int main(){
WelcomeMenu * myConWin = new WelcomeMenu();
delete myConWin;
return 0;
}
you can compile it like: g++ -lncurses -lform test.cpp
Thank you in advance for your response
The basic problem with the example is that it does not provide for displaying the characters. In ncurses, that would be done using the form_driver function:
form_driver
Once a form has been posted (displayed), you should funnel input events
to it through form_driver. This routine has three major input cases:
o The input is a form navigation request. Navigation request codes
are constants defined in <form.h>, which are distinct from the key-
and character codes returned by wgetch(3x).
o The input is a printable character. Printable characters (which
must be positive, less than 256) are checked according to the pro-
gram's locale settings.
o The input is the KEY_MOUSE special key associated with an mouse
event.
Making your program use form_driver would involve some reorganization of the program, moving that switch-statement into a function which can be called from the form-driver.
The ncurses examples include a few using form_driver. You might want to read through the test-code, seeing how the basic loop
while (!finished) {
switch (form_driver(form, c = form_virtualize(form, w))) {
case E_OK:
MvAddStr(5, 57, field_buffer(secure, 1));
clrtoeol();
refresh();
break;
case E_UNKNOWN_COMMAND:
finished = my_form_driver(form, c);
break;
default:
beep();
break;
}
uses its form_virtualize function to read characters (or form-requests). The printable characters that you are (not) seeing are handled by form_driver which updates the current field on the form. You can manipulate the field-buffer and do other special things as done in form_virtualize, but updating fields in the form is why you would use form_driver.
You need to use echo and noecho to turn echoing on/off. You could bake that part into an input function if you'd like:
#include <memory>
struct input_context {
input_context() {
echo(); // echo typed characters
curs_set(1); // show the cursor
}
~input_context() {
curs_set(0); // hide the cursor
noecho(); // turn off echoing
}
};
std::string get_string(WINDOW* win, size_t len) {
std::unique_ptr<char[]> buf = std::make_unique<char[]>(len);
input_context dummy;
if(wgetnstr(win, buf.get(), len) != ERR) return std::string{buf.get()};
return {};
}
Note 1: You should only use new/delete (and new[]/delete[]) when all other options have been depleted. In your main, you could just replace the dynamic creation of your WelcomeMenu with an automatic variable:
int main(){
WelcomeMenu myConWin;
}
Note 2: Use nullptr, not NULL.
fields[4] = nullptr; // was NULL
Note 3: As stated by #kebs in the comments, you should use the C++ versions of the header files:
#include <cassert> // was assert.h
#include <cstdio> // was stdio.h
#include <cstdlib> // was stdlib.h
Related
I have a few sets of forms within my ncurses program. Each field has A_UNDERLINE enabled but I noticed that only the last form posted will show underlines until I navigate to it; the others are simply blank space.
I would like to ensure all the fields appear with underlines as soon as I refresh my pad.
Eventually, I intend to allow users to add and remove fields within a form and will compile all field contents within a form together for analysis. Therefore putting all the fields into one form is possible but not ideal at all.
Since I'm new to curses, I'm a little lost as to what I should even be trying. I've tried calling form_driver(form, REQ_END_LINE); for each form prior to looping on input to no avail. As well as form_driver(form, REQ_NEXT_FIELD); since that seemed to work inside the input loop.
I'm clearly missing something but I'm not sure if I haven't correctly initialized my fields/forms or if I'm simply misunderstanding how these work.
I've created a simplified version of the issue below:
#include <ncurses.h>
#include <form.h>
int main()
{
WINDOW *pad;
FIELD *field[3];
FORM *my_form;
FIELD *field_two[3];
FORM *form_two;
int ch;
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
pad = newpad(LINES,COLS);
keypad(pad, TRUE);
field[0] = new_field(1, 10, 4, 18, 0, 0);
field[1] = new_field(1, 10, 6, 18, 0, 0);
field[2] = NULL;
field_two[0] = new_field(1, 10, 8, 18, 0, 0);
field_two[1] = new_field(1, 10, 10, 18, 0, 0);
field_two[2] = NULL;
set_field_back(field[0], A_UNDERLINE);
set_field_back(field[1], A_UNDERLINE);
set_field_back(field_two[0], A_UNDERLINE);
set_field_back(field_two[1], A_UNDERLINE);
wclear(pad);
my_form = new_form(field);
set_form_sub(my_form, pad);
post_form(my_form);
form_two = new_form(field_two);
set_form_sub(form_two, pad);
post_form(form_two);
mvwprintw(pad, 1, 1, "Some text");
mvwprintw(pad, 4, 10, "Value 1:");
mvwprintw(pad, 6, 10, "Value 2:");
mvwprintw(pad, 8, 10, "Value 3:");
mvwprintw(pad, 10, 10, "Value 4:");
prefresh(pad, 0, 0, 0, 0, LINES, COLS);
bool form1 = true;
while((ch = getch()) != KEY_F(1))
{
switch(ch)
{ case KEY_DOWN:
if (form1) {
form_driver(my_form, REQ_NEXT_FIELD);
form_driver(my_form, REQ_END_LINE);
} else {
form_driver(form_two, REQ_NEXT_FIELD);
form_driver(form_two, REQ_END_LINE);
}
break;
case KEY_UP:
if (form1) {
form_driver(my_form, REQ_PREV_FIELD);
form_driver(my_form, REQ_END_LINE);
} else {
form_driver(form_two, REQ_PREV_FIELD);
form_driver(form_two, REQ_END_LINE);
}
break;
case KEY_RIGHT:
form1 = false;
break;
case KEY_LEFT:
form1 = true;
break;
default:
if (form1) {
form_driver(my_form, ch);
} else {
form_driver(form_two, ch);
}
break;
}
prefresh(pad, 0, 0, 0, 0, LINES, COLS);
}
unpost_form(my_form);
free_form(my_form);
free_field(field[0]);
free_field(field[1]);
unpost_form(form_two);
free_form(form_two);
free_field(field_two[0]);
free_field(field_two[1]);
endwin();
return 0;
}
When the above is executed, I have a blank screen until I press a key (if you feel like pointing out why that is I'd like to hear but that's not the point of this question). After that, I see the fields for Value 3 and Value 4 have underlines but Value 1 and Value 2 do not until I start typing in them.
My goal here is to have underlines for fields on the same lines as Value 1 and Value 2 without any additional user input.
I was able to accomplish my goal of all fields being underlined by adding the following code immediately under post_form(form_two);
set_field_back(field[0], 0);
set_field_back(field[1], 0);
set_field_back(field[0], A_UNDERLINE);
set_field_back(field[1], A_UNDERLINE);
Ive been designing a roguelike video game in the console.
My problem is i initially draw the entire map with writeconsoleoutput at once then use writeconsoleoutputcharacter to redraw whats neccessary. But i have found that it draws an extra pixel thick on the right side.
This essentially erases one pixel off the tile on the right side.
Does anyone know a way to make it stop drawing a character and adding a 1 pixel blank space on the right side of the character.
EDIT: ONE WORKAROUND I HAVE FOUND IS TO REDRAW THE LINES THE PLAYER STEPS ON BUT IT ISNT PERFECT ONCE IN A WHILE I CAN NOTICE A FLICKER. Would be preferable to just redraw characters on an individual basis without any pixels attached on the end. or anywhere else.
The Code used to print the character
void printchar(char character, COORD location,short color)
{
const char *pointer = &character;
//short LENGTH = 1;
DWORD dwWritten = 0;
std::vector<WORD> attributes;attributes.clear();;
setAttributesChar(color,attributes);//Pushes back the color
if(attributes.size() == 1)
{ WriteConsoleOutputAttribute(GetStdHandle(STD_OUTPUT_HANDLE),&attributes[0], attributes.size(), location, &dwWritten);
WriteConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), pointer, attributes.size(), location, &dwWritten);
}
}
void setAttributesChar(short color,std::vector<WORD> &attributes)
{
switch(color)
{
case DEFAULTCOLOR:
attributes.push_back(FOREGROUND_INTENSE_WHITE);return;
case HOVEREDCOLOR:
attributes.push_back(FOREGROUND_GREEN);return;
case RED:
attributes.push_back(FOREGROUND_RED);return;
case GOLD:
attributes.push_back(FOREGROUND_INTENSE_YELLOW);return;
case REDWHITE:
attributes.push_back(COMBINED_REDWHITE);return;
case YELLOWPURPLE:
attributes.push_back(COMBINED_YELLOWPURPLE);return;
case NPCCOLOR:
attributes.push_back(FOREGROUND_INTENSE_CYAN);return;
case PURPLE:
attributes.push_back(FOREGROUND_MAGENTA);return;
case REDPURPLE:
attributes.push_back(COMBINED_REDPURPLE);return;
case BLUE:
attributes.push_back(FOREGROUND_BLUE);return;
case BLUEPURPLE:
attributes.push_back(COMBINED_BLUEPURPLE);return;
case INTENSEGREEN:
attributes.push_back(FOREGROUND_INTENSE_GREEN);return;
case REDGREEN:
attributes.push_back(COMBINED_REDGREEN);return;
case BWHITE:
attributes.push_back(BACKGROUND_WHITE);return;
}
}
EXAMPLE:
BEFORE CHARACTER WALKS OVER ROAD: BEFORE IMAGE
AFTER CHARACTER WALKS OVER ROAD: AFTER IMAGE
EXECUTABLE LINK: EXECUTABLE
I have created a working example that you guys can compile(Assuming u use windows) to see what is occuring. In it i create a console set the size fill the screen with the asci symbol 223. then i use Writeconsoleoutchar to print a green 223 symbol. You can see the extra drawn black pixel line.
#include <windows.h>
#include <cwchar>
#include <vector>
enum COLORS{HOVEREDCOLOR};
void setAttributesChar(short color,std::vector<WORD> &attributes)
{
switch(color)
{
case HOVEREDCOLOR:
attributes.push_back(FOREGROUND_GREEN);return;
}
}
void printchar(char character, COORD location,short color)
{
const char *pointer = &character;
//short LENGTH = 1;
DWORD dwWritten = 0;
std::vector<WORD> attributes;attributes.clear();;
setAttributesChar(color,attributes);//Pushes back the color
if(attributes.size() ==1)
{ WriteConsoleOutputAttribute(GetStdHandle(STD_OUTPUT_HANDLE),&attributes[0], attributes.size(), location, &dwWritten);
WriteConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), pointer, attributes.size(), location, &dwWritten);
}
}
#define SCREENWIDTH 1550
#define SCREENHEIGHT 850
void SETWINDOWUP(DWORD &SIZE)
{
CONSOLE_SCREEN_BUFFER_INFO info;
MoveWindow(GetConsoleWindow(),0,0,SCREENWIDTH,SCREENHEIGHT,true); //MOVE WINDOW TO 0,0 AND MAKE 1600 BY 880
::SetWindowPos(GetConsoleWindow(), HWND_TOPMOST, 0, 0, 0,0, SWP_DRAWFRAME | SWP_NOREPOSITION | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); //SET WNDOWS POSITION COMPARED TO OTHER WINDOWS IN DRAW ORDER
::SetWindowPos(GetConsoleWindow(),HWND_NOTOPMOST, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
HANDLE hout = GetStdHandle( STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hout,&info);
COORD newBuffersize =
{
info.srWindow.Right - info.srWindow.Left + 1,
info.srWindow.Bottom - info.srWindow.Top + 1
};
SetConsoleScreenBufferSize(hout,newBuffersize);
SIZE = info.dwSize.X * info.dwSize.Y;
//EDIT REGULATED FONT SIZE
CONSOLE_FONT_INFOEX cfi;
std::wcscpy(cfi.FaceName, L"Lucida Console"); // Choose your font
cfi.FontFamily = FF_DONTCARE;
cfi.nFont = 0;
cfi.dwFontSize.X = 0;
cfi.dwFontSize.Y = 25; // Height
cfi.FontWeight = FW_BOLD;
cfi.cbSize = sizeof(cfi);
SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), false, &cfi);
}
int main()
{
Sleep(500);DWORD SIZE = 0;SETWINDOWUP(SIZE);
DWORD charswritten;COORD start = {0,0};TCHAR CHARACTER = 223;
FillConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE),CHARACTER,SIZE,start,&charswritten);
Sleep(500);
printchar(223,{6,5},HOVEREDCOLOR);
Sleep(500);
return 0;
};
Game dev class. Trying to understand what i did wrong here. Did I add the wrong thing, or do i have it at the wrong place.
my goal was to add music.
#include "allegro5/allegro.h"
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_ttf.h>
#include <allegro5/allegro_native_dialog.h>
#include <stdio.h>
#include <string>
#include <allegro5\allegro_audio.h>
#include <allegro5\allegro_acodec.h>
#define FPS 60
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
#define BLACK al_map_rgb(0, 0, 0)
#define WHITE al_map_rgb(255, 255, 255)
//Prototypes
bool initializeAllegro();
//Essential Allegro pointers
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_EVENT_QUEUE *eventQueue = NULL;
ALLEGRO_TIMER *timer = NULL;
ALLEGRO_FONT *arial14;
int main()
{
ALLEGRO_BITMAP *backgroundImage; //Bitmap for the background image(star field)
int backgroundImageWidth = 0, backgroundImageHeight = 0;
bool redrawRequired = true;
//Using std:string for name, so length is not a factor
std::string userName = "";
std::string prompt1 = "Enter your Player's name below";
std::string prompt2 = "(Letters & digits only. Backspace to erase.)";
std::string prompt3 = "Press Enter to accept...";
std::string prompt4 = "Press Escape to exit...";
char keyToAdd = ' ';
bool enteringUserName = true;
bool quit = false;
//Initialize allegro, etc
if (!initializeAllegro()) return -1;
//load the arial font
arial14 = al_load_font("arial-mt-black.ttf", 14, 0);
if (!arial14)
{
return -3;
}
//test running music
al_init_acodec_addon();
ALLEGRO_SAMPLE *song = al_load_sample("hideandseek.wav");
ALLEGRO_SAMPLE_INSTANCE *songInstance = al_create_sample_instance(song);
al_set_sample_instance_playmode(songInstance, ALLEGRO_PLAYMODE_LOOP);
al_attach_sample_instance_to_mixer(songInstance, al_get_default_mixer());
//Load the background picture
if (!(backgroundImage = al_load_bitmap("starbackground.bmp")))
{
return -5;
}
//Get dimensions of the background image
backgroundImageWidth = al_get_bitmap_width(backgroundImage);
backgroundImageHeight = al_get_bitmap_height(backgroundImage);
//Set the back buffer as the drawing bitmap for the display
al_set_target_bitmap(al_get_backbuffer(display));
//Initialize the event queue
eventQueue = al_create_event_queue();
if (!eventQueue)
{
al_destroy_display(display);
al_destroy_timer(timer);
return -1;
}
//Register events as arriving from these sources: display, timer, keyboard
al_register_event_source(eventQueue, al_get_display_event_source(display));
al_register_event_source(eventQueue, al_get_timer_event_source(timer));
al_register_event_source(eventQueue, al_get_keyboard_event_source());
al_flip_display();
//play song
al_play_sample_instance(songInstance);
//Start up the timer. Don't do this until just before the game is to start!
al_start_timer(timer);
//This would be a loop solely for entering the user's name, not starting the game
//Allegro keycodes are laid out as follows. (Full set of key codes is included in keycodes.h)
//Key codes for capital letters are 1 - 26 for A - Z
//Digit values are 27 - 36 for the top digits (0 - 9), and 37 - 46 for the keypad
while (!quit)
{
ALLEGRO_EVENT evt;
//Wait for one of the allegro-defined events
al_wait_for_event(eventQueue, &evt);
//An event was generated. Check for all possible event types
switch (evt.type)
{
case ALLEGRO_EVENT_KEY_CHAR:
if (evt.keyboard.keycode >= 1 && evt.keyboard.keycode <= 26) //It's a capital letter
{
//Convert to ASCII capital
keyToAdd = *al_keycode_to_name(evt.keyboard.keycode);
userName += keyToAdd;
}
else if (evt.keyboard.keycode >= 27 && evt.keyboard.keycode <= 36)
{
//Convert to digit
keyToAdd = evt.keyboard.keycode + 21;
userName += keyToAdd;
}
//Handle digits on keypad
else if (evt.keyboard.keycode >= 37 && evt.keyboard.keycode <= 46)
{
//Convert to digit
keyToAdd = evt.keyboard.keycode + 11;
userName += keyToAdd;
}
else //Handle a few other keys
{
switch(evt.keyboard.keycode)
{
case ALLEGRO_KEY_BACKSPACE:
if (userName.length() > 0)
userName = userName.substr(0, userName.length() - 1);
break;
//Enter key stops username entry
case ALLEGRO_KEY_ENTER:
case ALLEGRO_KEY_PAD_ENTER:
enteringUserName = false;
break;
case ALLEGRO_KEY_ESCAPE:
quit = true;
break;
}
}
break;
case ALLEGRO_EVENT_DISPLAY_CLOSE:
quit = true;
break;
case ALLEGRO_EVENT_TIMER:
redrawRequired = true;
break;
}//END switch evt.type
//Rerender the entire scene
if (redrawRequired && al_is_event_queue_empty(eventQueue))
{
redrawRequired = false;
al_clear_to_color(BLACK); //Just in case
//Draw background
al_draw_scaled_bitmap(backgroundImage, 0, 0, backgroundImageWidth, backgroundImageHeight,
0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0);
//Prompt for name to be entered
al_draw_textf (arial14, WHITE, 0, 30, 0, "%s", prompt1.c_str());
al_draw_textf (arial14, WHITE, 0, 50, 0, "%s", prompt2.c_str());
if (userName.length() > 0)
{
if (enteringUserName)
{
al_draw_textf (arial14, WHITE, 0, 70, 0, "%s", userName.c_str());
al_draw_textf (arial14, WHITE, 0, 90, 0, "%s", prompt3.c_str());
}
else
{
al_draw_textf (arial14, WHITE, 0, 70, 0, "You entered your name as-->%s", userName.c_str());
}
}
al_draw_textf(arial14, WHITE, 0, 400, 0, "%s", prompt4.c_str());
al_flip_display();
}//END rendering the screen
}//END input Loop
//Release all dynamically allocated memory
al_destroy_bitmap(backgroundImage);
al_destroy_font(arial14);
al_destroy_timer(timer);
al_destroy_display(display);
al_destroy_event_queue(eventQueue);
//destroy songs
al_destroy_sample(song);
al_destroy_sample_instance(songInstance);
return 0;
}//END main
//Take care of Allegro initialization tasks
//Return false if any fail.
bool initializeAllegro()
{
bool success = true;
//Init basic environment
if (!al_init())
{
al_show_native_message_box(NULL, "ERROR", "Allegro failed to initialize!", NULL, NULL, NULL);
success = false;
}
//Initialize keyboard input
if (!al_install_keyboard())
{
al_show_native_message_box(NULL, "ERROR", "Keyboard failed to initialize!", NULL, NULL, NULL);
success = false;
}
//Initialize timer
timer = al_create_timer(1.0 / FPS);
if (!timer)
{
al_show_native_message_box(NULL, "ERROR", "Timer failed to initialize!", NULL, NULL, NULL);
success = false;
}
//Initialize display
display = al_create_display(SCREEN_WIDTH, SCREEN_HEIGHT);
if (!display)
{
al_show_native_message_box(NULL, "ERROR", "Display failed to initialize!", NULL, NULL, NULL);
success = false;
}
//Initialize the image mechanism
if (!al_init_image_addon())
{
al_show_native_message_box(NULL, "ERROR", "Image addon failed to initialize!", NULL, NULL, NULL);
success = false;
}
al_init_font_addon();
al_init_ttf_addon();
return success;
}//END initializeAllegro
Any wav file should work.
i get the error message -
First-chance exception at 0x0F196486
(allegro-5.0.10-monolith-md-debug.dll) in User Input Project.exe:
0xC0000005: Access violation reading location 0x00000000. Unhandled
exception at 0x0F196486 (allegro-5.0.10-monolith-md-debug.dll) in User
Input Project.exe: 0xC0000005: Access violation reading location
0x00000000.
The program '[9596] User Input Project.exe' has exited with code 0
(0x0).
I mentioned to step through things with a debugger in my comment, but the code smell here is this:
ALLEGRO_SAMPLE *song = al_load_sample("hideandseek.wav");
ALLEGRO_SAMPLE_INSTANCE *songInstance = al_create_sample_instance(song);
You should Definitely be checking for (song == NULL) before you call al_create_sample_instance()
If your path to the .WAV file is wrong or if the loading fails for any other reason, then al_load_sample() will return NULL, and you shouldn't be trying to do anything with song
(Sorry for my bad English)
Well, I tried with PeekConsoleInput() and works. But, if I don't press any key, this returns me zeroes including cords and ASCII key. I want that return the actual position of the mouse cursor without press any key or move the mouse and without the "zeroes" and without a pause the code
This is a fragment of the main program (because is very big):
void inputhit(void)
{
#ifndef ENABLE_QUICK_EDIT_MODE
#define ENABLE_QUICK_EDIT_MODE 0x0040
#endif
//static DWORD old_mode;
static DWORD new_mode;
static DWORD readed;
static INPUT_RECORD ir[1];
static int clic;
static int key;
screen.in = GetStdHandle(STD_INPUT_HANDLE);
// GetConsoleMode(screen.in, &old_mode);
/* keep current configuration,
* but enable mouse input,
* disable quick edit mode,
* disable ctrl+c
*/
new_mode =
(DWORD) (( ENABLE_MOUSE_INPUT) & ~ENABLE_QUICK_EDIT_MODE
& ~ENABLE_PROCESSED_INPUT);
SetConsoleMode(screen.in, new_mode);
mouse_in_hit:
PeekConsoleInput(screen.in, ir, 1, &readed);
if (ir[0].EventType == KEY_EVENT) {
key = ir[0].Event.KeyEvent.uChar.AsciiChar;
if (key == 0) key = ir[0].Event.KeyEvent.wVirtualKeyCode + 255;
}
clic = (int) (ir[0].Event.MouseEvent.dwButtonState);
printf("%d %d %d %d", (int) ir[0].Event.MouseEvent.dwMousePosition.Y,
(int) ir[0].Event.MouseEvent.dwMousePosition.X,
(int) clic,
(int) key,
);
FlushConsoleInputBuffer(screen.in);
}
The result are:
When open the program from a console command in a loop, I move the mouse, and returns a large list of coords, eg:
[Y X Mouseclick Key ]
10 10 1 0 <--- this result when the press or moves the mouse
0 0 0 0 <--- this result when is not pressed or moved the mouse
21 13 2 0
...
And that I want:
[Y x MouseClick Key ]
10 23 0 0 <-- this result when the mouse is in this position on the window (without moving it)
12 9 1 0 <-- this result when click
I wouldn't use PeekConsoleInput for this, I'd use a combination of GetNumberOfConsoleInputEvents to decide if there is input available and ReadConsoleInput to retrieve and process it.
I tweaked the MSDN Example a bit to read/process when there is input available and to do other stuff when there isn't. I assume you're polling for input and you could adapt this to work that way as well.
#include <windows.h>
#include <stdio.h>
#include <vector>
#ifndef MOUSE_HWHEELED
#define MOUSE_HWHEELED 0x0008
#endif
HANDLE hStdin;
DWORD fdwSaveOldMode;
void ErrorExit(LPSTR lpszMessage)
{
fprintf(stderr, "%s\n", lpszMessage);
SetConsoleMode(hStdin, fdwSaveOldMode);
ExitProcess(0);
}
bool KeyEventProc(const KEY_EVENT_RECORD& ker)
{
printf("key %s: %c\n", ker.bKeyDown ? "pressed" : "released", ker.uChar.AsciiChar);
return ker.uChar.AsciiChar == 'q';
}
void MouseEventProc(const MOUSE_EVENT_RECORD& mer)
{
const int X = mer.dwMousePosition.X;
const int Y = mer.dwMousePosition.Y;
const int dir = static_cast<int>(mer.dwButtonState) >> 16;
switch(mer.dwEventFlags)
{
case 0:
if(mer.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED)
{
printf("left button press # %d %d\n", X, Y);
}
else if(mer.dwButtonState == RIGHTMOST_BUTTON_PRESSED)
{
printf("right button press # %d %d\n", X, Y);
}
else
{
//You'd need more code here to track releases or more buttons.
printf("other button press/release # %d %d\n", X, Y);
}
break;
case DOUBLE_CLICK:
printf("double click # %d %d\n", X, Y);
break;
case MOUSE_HWHEELED:
printf("horizontal mouse wheel %s\n", dir >= 0 ? "right" : "left");
break;
case MOUSE_WHEELED:
printf("vertical mouse wheel %s\n", dir >= 0 ? "up" : "down");
break;
case MOUSE_MOVED:
printf("mouse moved: %d %d\n", mer.dwMousePosition.X, mer.dwMousePosition.Y);
break;
default:
printf("unknown mouse event\n");
break;
}
}
int main()
{
hStdin = GetStdHandle(STD_INPUT_HANDLE);
if(hStdin == INVALID_HANDLE_VALUE)
ErrorExit("GetStdHandle");
if(!GetConsoleMode(hStdin, &fdwSaveOldMode))
ErrorExit("GetConsoleMode");
DWORD fdwMode = ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT;
if(!SetConsoleMode(hStdin, fdwMode))
ErrorExit("SetConsoleMode");
std::vector<INPUT_RECORD> irInBuf;
DWORD numEventsAvailable;
DWORD lastTickCount = GetTickCount();
bool finished = false;
while(!finished)
{
GetNumberOfConsoleInputEvents(hStdin, &numEventsAvailable);
if(numEventsAvailable)
{
irInBuf.resize(numEventsAvailable);
DWORD cNumRead;
ReadConsoleInput(hStdin, &irInBuf[0], irInBuf.size(), &cNumRead);
for(DWORD i = 0; i < cNumRead; i++)
{
switch(irInBuf[i].EventType)
{
case KEY_EVENT:
finished = KeyEventProc(irInBuf[i].Event.KeyEvent);
break;
case MOUSE_EVENT:
MouseEventProc(irInBuf[i].Event.MouseEvent);
break;
default:
break;
}
}
}
else
{
if(GetTickCount() - lastTickCount > 1000)
{
lastTickCount = GetTickCount();
printf("Press 'q' to exit\n");
}
}
}
SetConsoleMode(hStdin, fdwSaveOldMode);
return 0;
}
I've started making a simple game in c++.
At this stage I have all the characters and attributes (tiles) stored in a file that is then loaded in to CHAR_INFO. Then using the WriteConsoleOutputA I can basically paint the map/world.
That all appears to work correctly and there is no display issues. However when I start to move my character around he is mucking up the tiles. It a bit hard to explain so here is a little video. http://www.youtube.com/watch?v=v5Be5qz30r8.
As you can see there is not problem with the green tiles (foreground and background are same colour). But when I go near the grey tiles it isn't working as intended.
main.cpp snippet...
int main()
{
while (0 < 1)
{
if (GetAsyncKeyState(VK_UP) || GetAsyncKeyState(0x57))
player.Move('u');
if (GetAsyncKeyState(VK_DOWN) || GetAsyncKeyState(0x53))
player.Move('d');
if (GetAsyncKeyState(VK_LEFT) || GetAsyncKeyState(0x41))
player.Move('l');
if (GetAsyncKeyState(VK_RIGHT) || GetAsyncKeyState(0x44))
player.Move('r');
Sleep(100);
}
}
Character class snippet
void Character::Move(char d)
{
COORD oldLoc = loc;
char walkableTiles[12] = {
4, 179, 191, 192, 193, 194, 195, 196, 197, 217, 218, 255
};
//set character
switch (d)
{
case 'u' :
loc.Y--;
break;
case 'd':
loc.Y++;
break;
case 'l':
loc.X--;
break;
case 'r':
loc.X++;
break;
}
//setup the buffer and location of tiles
CHAR_INFO newTile[1];
COORD buffSize = {1,1};
COORD buffPos = {0,0};
SMALL_RECT newBuffArea = {loc.X, loc.Y, loc.X, loc.Y};
SMALL_RECT oldBuffArea = {oldLoc.X, oldLoc.Y, oldLoc.X, oldLoc.Y};
//store newTile info
ReadConsoleOutputA(outHnd, newTile, buffSize, buffPos, &newBuffArea);
//check if newTile is valid
bool valid = 0; // assume not valid
for (int i=0; i < sizeof(walkableTiles); i++)
{
if (newTile[0].Char.AsciiChar == walkableTiles[i])
valid = 1;
}
if (!valid)
{
loc = oldLoc;
return;
}
//restore oldTile information
WriteConsoleOutputA(outHnd, oldTile, buffSize, buffPos, &oldBuffArea);
//newTile becomes oldTile
oldTile[0] = newTile[0];
//get bg of newTile and set character/attributes
int bg = (newTile[0].Attributes & Blue << BG ) | (newTile[0].Attributes & Red << BG) | (newTile[0].Attributes & Green << BG) | (newTile[0].Attributes & BACKGROUND_INTENSITY);
newTile[0].Char.AsciiChar = 1;
newTile[0].Attributes = White | bg;
//place character on newTile
WriteConsoleOutputA(outHnd, newTile, buffSize, buffPos, &newBuffArea);
Apologies for the code, this is my first game.