I'm having a problem with the Simple Directmedia Layer library. The following code draws a block on the screen when a mouse button is pressed:
SDL_Event event;
while(running){
while(SDL_PollEvent(&event)){
while(event.button.state == SDL_PRESSED){
SDL_PollEvent(&event);
//where to draw
boxRect.x = event.motion.x;
boxRect.y = event.motion.y;
//Draw to screen
SDL_FillRect(display,&boxRect,boxColor);
SDL_Flip(display);
}
// ...
}
// ...
}
It works fine until I move the mouse, why moving the mouse makes event.button.state untrue?
How can I work with both simultaneously (i.e. keep drawing while the button is pressed)?
The problem with your code is that you're calling SDL_PollEvent (documented here) twice. As said in the documentation:
If event is not NULL, the next event is removed from the queue and
stored in the SDL_Event structure pointed to by event.
Rearranging your code a bit, like getting rid of the second SDL_PollEvent, creating the proper flow for clicking, moving, releasing and extracting rendering from the input pumping should give you something like this:
SDL_Event Event;
while(running)
{
while(SDL_PollEvent(&Event))
{
switch(Event.type)
{
// Handle your drawing with a simple state machine:
case SDL_MOUSEBUTTONDOWN:
{
if(Event.button.button == SDL_BUTTON_LEFT)
{
if(stateMachine == Released)
{
// ... begin drawing
stateMachine = Dragging
}
}
break;
}
case SDL_MOUSEMOTION:
{
if(stateMachine == Dragging)
{
// ... update the extends of your rect
}
}
case SDL_MOUSEBUTTONUP:
{
if(Event.button.button == SDL_BUTTON_LEFT)
{
if(stateMachine != Released)
{
// ... finalize drawing... add the rect to a list? flush it?
stateMachine = Released;
}
}
}
case SDL_QUIT:
{
running = false;
break;
}
}
}
// Outside of your event pumping, update the graphics
SDL_FillRect(display,&boxRect,boxColor);
SDL_Flip(display);
}
event.button.state is a bit mask. It is declared as a char which can hold eight bits representing different states of the buttons.
typedef struct{
Uint8 type;
Uint8 state;
Uint16 x, y;
Sint16 xrel, yrel;
} SDL_MouseMotionEvent;
More information here,
http://www.libsdl.org/docs/html/sdlgetmousestate.html
http://www.libsdl.org/docs/html/sdlmousemotionevent.html
Thanks to emartel's answer, it's working as expected, here's the code:
SDL_Event event;
int drawing = 0;
while(running){
while(SDL_PollEvent(&event)){
switch(event.type){
//starts drawing when any mouse button is pressed
case SDL_MOUSEBUTTONDOWN:
drawing = 1;
boxRect.x = event.button.x;
boxRect.y = event.button.y;
break;
//keeps drawing while no button is released
case SDL_MOUSEMOTION:
if(drawing == 1){
boxRect.x = event.motion.x;
boxRect.y = event.motion.y;
}
break;
//stops drawing when any button is released
case SDL_MOUSEBUTTONUP:
drawing = 0;
break;
case SDL_QUIT:
running = 0;
break;
}
}
//draws to screen
if(drawing == 1){
SDL_FillRect(display,&boxRect,boxColor);
SDL_Flip(display);
}
}
Related
I'm recreating the game 'super hexagon' in c using sdl2 library.
I have drawn the circle in the middle of the application and the little cursor(not the mouse but the triangle around the circle) that is supposed to move around it when the player interact with the keyboard (left and right keys) like so:
void Game_run(struct Game *game)
{
//Previous rendering are cleared.
SDL_RenderClear(game->renderer);
//-------------------------------------------------------
if (filledCircleRGBA(game->renderer,SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 10,255, 255, 255, 255) != 0)
{
fprintf(stderr, "Error: the circle was not rendered\n");
SDL_DestroyRenderer(game->renderer);
}
arcRGBA( game->renderer, SCREEN_WIDTH/2, SCREEN_HEIGHT/2, 100, 0, 80, 255, 255, 255, 255 );
filledTrigonRGBA (game->renderer, CURSOR_POSITION_X-10, CURSOR_POSITION_Y, CURSOR_POSITION_X+10, CURSOR_POSITION_Y, CURSOR_POSITION_X, CURSOR_POSITION_Y+10, 255, 255, 255, 255);
SDL_RenderPresent(game->renderer);
//-------------------------------------------------------
//We are payng
game->state = PLAYING;
SDL_Event event;
SDL_bool GameRunning = SDL_TRUE;
while (GameRunning) //while the player didnt loose, the game is running
{
while (SDL_PollEvent(&event))
{
/* code */
switch (event.type)
{
//when a key is pressed
case SDL_KEYDOWN:
//Detecting the pressed key
switch (event.key.keysym.sym)
{
case SDLK_LEFT:
printf("Left key pressed\n");
break;
case SDLK_RIGHT:
printf("Right key pressed\n");
break;
default:
break;
}
break;
//when a key is unpressed
case SDL_KEYUP:
switch (event.key.keysym.sym)
{
case SDLK_LEFT:
printf("Left key up\n");
break;
case SDLK_RIGHT:
printf("Right key up\n");
break;
default:
break;
}
break;
//if the user quit the game
case SDL_QUIT:
//The game is over
GameRunning = SDL_FALSE;
game->state = GAME_OVER;
break;
default:
break;
}
}
}
}
I have already implemented the event listener to detect the user input.
But I have no idea how to animate those elements.
How should I approach the problem? I want to understand how animation works when i render elements using gfx functions..do i need to load the drawn elements into a SDL_Surface first?
Problem
I need to resize no border form,But when ChromiumWebBrowser's dock is fill, CEF will eat mouse related information, dragging the form becomes very insensitive.
I observed Google Chrome, when the mouse outside the form can be dragged to change the size of the form, how can I achieve the same effect as Google Chrome.
Update
I need drag to change form's size,and Draggable borderless window in CefSharp a lot of private code, I can't know how to implement it.for example, ChromeWidgetMessageInterceptor.SetupLoop,in the cefsharp source code, ChromeWidgetMessageInterceptor does not have SetupLoop.
Update2 Add the screenshot and the code
const int WM_MOUSEMOVE = 0x0200;
const int WM_MOUSELEAVE = 0x02A3;
const int WM_LBUTTONDOWN = 0x0201;
//
switch (message.Msg)
{
case WM_MOUSEMOVE:
var clientCursorPos = GetMousePoint(MousePosition);
var newE = new MouseEventArgs(MouseButtons.None, 0, clientCursorPos.X, clientCursorPos.Y, 0);
this.InvokeOnParent(delegate { this.OnMouseMove(newE); });
break;
case WM_LBUTTONDOWN:
if (Cursor != Cursors.Default)
{
//this.InvokeOnParent(delegate { this.ResizeForm(this.ResizeDir); });
this.InvokeOnParent(delegate
{
if (DesignMode) return;
var dir = -1;
switch (this.ResizeDir)
{
case ResizeDirection.BottomLeft:
dir = HTBOTTOMLEFT;
break;
case ResizeDirection.Left:
dir = HTLEFT;
break;
case ResizeDirection.Right:
dir = HTRIGHT;
break;
case ResizeDirection.BottomRight:
dir = HTBOTTOMRIGHT;
break;
case ResizeDirection.Bottom:
dir = HTBOTTOM;
break;
}
ReleaseCapture();
if (dir != -1)
{
SendMessage(Handle, WM_NCLBUTTONDOWN, dir, 0);
}
});
}
break;
case WM_MOUSELEAVE:
Console.WriteLine("WM_MOUSELEAVE");
break;
}
Mouse drag to change the size of the problem
I'm trying to develop an event handler in C using SDL.
I wanted to be able to detect the following things: Key pressed, key released and key held down.
The problem is that keyboard auto-repeat spams pressed keys after a certain delay and at a given interval. To mitigate this I tried using SDL_EnableKeyRepeat(0, 0); which according to documentation should disable it?
As that failed I tried mapping SDL_GetKeyboardState(NULL); and it sort of works. But I'm having the issue that if i hold down a key for X seconds, it will spam ghost events for the same amount of time.
An example output from pressing esc, holding it down for a bit, and then releasing it is:
Pressed
HELD
HELD
HELD
...
RELEASED {it breaks down after first proper release}
PRESSED
RELEASED
HELD
PRESSED
RELEASED
HELD
...
RELEASED // it always ends on released.
The result from above is:
Pressed: 18;
Released: 21;
Held: 39.
Should be:
Pressed 1;
Released 1;
Held: say 20.
Not sure if I'm messing up my logic somehow, or if it's something with the actual built in mechanics of keyboard events.
Here is the code:
/**
Program Interface
void ev_handle_start() // Starts the Event Handler.
void ev_handle_stop() // Pauses the Event Handler.
**/
#include <stdio.h>
#include <SDL2/SDL.h>
#include <SDL/SDL.h>
#include "ev_handler.h"
#define PRESSED 1
#define RELEASED 0
#define TMR_FREQ 33
#define MOUSE_RIGHT 1
#define MOUSE_LEFT 3
#define EV_LEFT_CLICK 0
#define EV_LEFT_RELEASE 1
#define EV_RIGHT_CLICK 2
#define EV_RIGHT_RELEASE 3
#define EV_MOUSE_MAX 4
#define MAX_SCANCODES SDL_SCANCODE_MODE
#define EV_DELAY 70
//********************************************************************************************//
// VARIABLES
//********************************************************************************************//
SDL_Event e;
Uint8* keyboardState;
SDL_TimerID ev_timer = NULL; // Timer to callback the search of the code
bool ev_handler_active = false; // Used to determine if the ev_handler() is enabled or not
bool buttonState[EV_MOUSE_MAX]; // Button Stats, either pressed or released
bool evMouseEvent[EV_MOUSE_MAX]; // Used to flag if a mouse events
int evKeyEventStatus[MAX_SCANCODES];
bool evKeyStatePrev[MAX_SCANCODES]; //
bool evKeyStateCur[MAX_SCANCODES]; //
//********************************************************************************************//
// 1 FRONTEND
//********************************************************************************************//
//********************************************************************************************//
// 1.1 MOUSE
// Can only handle left and right mouse buttons.
//********************************************************************************************//
bool evButtonPressed(int button)
{ bool flag = button == SDL_BUTTON_RIGHT || button == SDL_BUTTON_LEFT;
if (!flag) {
printf("ERROR: isButtonPressed(int) received parameter of invalid type.\n");
return false;
}
return buttonState[button];
}
bool evLeftClick()
{
return evMouseEvent[EV_LEFT_CLICK];
}
bool evLeftRelease()
{
return evMouseEvent[EV_LEFT_RELEASE];
}
bool evRightClick()
{
return evMouseEvent[EV_RIGHT_CLICK];
}
bool evRightRelease()
{
return evMouseEvent[EV_RIGHT_RELEASE];
}
//********************************************************************************************//
// 1.2 KEYBOARD
//********************************************************************************************//
bool evKeyHeld(SDL_Scancode sc)
{
return evKeyEventStatus[sc] == 1 || evKeyEventStatus[sc] == 3;
}
bool evKeyPressed(SDL_Scancode sc)
{
return evKeyEventStatus[sc] == 1;
}
bool evKeyReleased(SDL_Scancode sc)
{
return evKeyEventStatus[sc] == 2;
}
//********************************************************************************************//
// 1.3 MANAGE
//********************************************************************************************//
void evSetup()
{
for (int i = 0; i < MAX_SCANCODES; i++) {
evKeyStatePrev[i] = 0;
}
evClearEvents();
}
int j;
void evClearEvents()
{
evMouseEvent[EV_LEFT_CLICK] = false;
evMouseEvent[EV_LEFT_RELEASE] = false;
evMouseEvent[EV_RIGHT_CLICK] = false;
evMouseEvent[EV_RIGHT_RELEASE] = false;
}
//********************************************************************************************//
// 2 BACKEND
//********************************************************************************************//
void ev_handler()
{
while(SDL_PollEvent(&e))
{
switch (e.type)
{
/*Skip unnecessary events*/
case SDL_KEYDOWN:
break;
case SDL_KEYUP:
break;
case SDL_MOUSEMOTION:
break;
case SDL_MOUSEBUTTONDOWN:
buttonState[e.button.button] = PRESSED;
if (e.button.button == SDL_BUTTON_LEFT) {
evMouseEvent[EV_LEFT_CLICK] = true;
}
else if (e.button.button == SDL_BUTTON_RIGHT) {
evMouseEvent[EV_RIGHT_CLICK] = true;
}
break;
case SDL_MOUSEBUTTONUP:
buttonState[e.button.button] = RELEASED;
if (e.button.button == SDL_BUTTON_LEFT) {
evMouseEvent[EV_LEFT_RELEASE] = true;
}
else if (e.button.button == SDL_BUTTON_RIGHT) {
evMouseEvent[EV_RIGHT_RELEASE] = true;
}
break;
case SDL_QUIT:
exit(0);
printf("Quit\n");
break;
default:
break;
}
keyboardState = SDL_GetKeyboardState(NULL);
for (int i = 0; i < MAX_SCANCODES; i++)
{
evKeyStateCur[i] = keyboardState[i];
int sum = evKeyStateCur[i] + evKeyStatePrev[i]*2;
switch (sum) {
case 0:
break;
case 1:
evKeyEventStatus[i] = 1;
break;
case 2:
evKeyEventStatus[i] = 2;
break;
case 3:
evKeyEventStatus[i] = 3;
break;
default: evKeyEventStatus[i] = 0; break;
}
evKeyStatePrev[i] = evKeyStateCur[i];
}
}
}
The problem was that SDL 2.0.4 had a bug where holding down a key sent key releases in-between key presses instead of continuous key presses. Downloading the latest version fixed the issue.
I am currently programming a simple game in SDL (PONG clone) where i must have a continuously key pressing.
The problem I have is that when i press a button it reacts, waits a short period and then continues to continuously key pressing.
I wonder how i get that little delay to go away.
Here is my code:
while (running)
{
while (SDL_PollEvent(&event))
{
//update the game
if (event.type == SDL_QUIT)
{
quit_game();
running = false;
}
if(event.type == SDL_KEYDOWN)
{
switch (event.key.keysym.sym)
{
case SDLK_DOWN:
move_paddle(UP);
break;
case SDLK_UP:
move_paddle(DOWN);
break;
default:
break;
}
}
}
draw_game();
move_ball();
SDL_Delay(15);
}
The first thing i did was i wanted to remove the small delay and get the movement of the paddle smoother. I introduced 2 new global bool variables to keep track on which key that was pressed down.
bool key_up = false;
bool key_down = false;
I initiate them with the value false because no key is currently pressed.
In my switch I set the value for these boolean variables to match what is currently pressed down.
if(event.type == SDL_KEYDOWN)
{
switch (event.key.keysym.sym)
{
case SDLK_DOWN:
key_up = true;
key_down = false;
break;
case SDLK_UP:
key_down = true;
key_up = false;
break;
default:
break;
}
}
Then in the first while-loop i make two if-cases for the two keys that can be pressed and call the movement function in these if-cases.
if (key_up)
move_paddle(UP);
else if (key_down)
move_paddle(DOWN);
When i did this the delay disappeared and the paddle movement was smooth but it also generated another probelm, the paddle could not stay still in one place (except the boundraries). I then realised that the code is only looking for a key that is pressed down and does not react to a key that is released.
I then figured out that if a key is released (in this case the UP key or the DOWN key) the two boolean values had to be set to false because no key was pressed down.
What I did was that I made another if-statement after the if(event.type == SDL_KEYDOWN) that would disable the movement of the paddle if no key was pressed down.
if (event.type == SDL_KEYUP)
{
key_down = false;
key_up = false;
}
When I implemented all of this in my code the game worked like a charm.
For those who are interested, here is my new final code:
int gameloop()
{
SDL_Event event;
bool running = true;
bool key_up = false;
bool key_down = false;
while (running)
{
while (SDL_PollEvent(&event))
{
//update the game
if (event.type == SDL_QUIT)
{
quit_game();
running = false;
}
if(event.type == SDL_KEYDOWN)
{
switch (event.key.keysym.sym)
{
case SDLK_DOWN:
key_up = true;
key_down = false;
break;
case SDLK_UP:
key_down = true;
key_up = false;
break;
default:
break;
}
}
if (event.type == SDL_KEYUP)
{
key_down = false;
key_up = false;
}
}
if (key_up)
move_paddle(UP);
else if (key_down)
move_paddle(DOWN);
move_ball();
ai_paddle_move();
draw_game();
printf("Player: %d | AI: %d\n", player, ai);
SDL_Delay(15);
}
return 1;
}
i'm trying to make a custom windows 8 style button using windows C API. however when i try to draw the text on the button after i redraw the button nothing visible happens!
I even tried creating a static window as a child of the button and sending WS_ERASEBKGND message to it when i redraw the button but as soon as i redraw the button for the first time it disappears!
here is the window procedure for my custom button:
LRESULT CALLBACK ButtonWindowProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
//static HWND static_text_handle;
HDC hdc;
PAINTSTRUCT ps;
DWORD color;
HFONT font,holdFont;
static RECT rect;
wchar_t test[] = L"test";
TRACKMOUSEEVENT tme = {sizeof(TRACKMOUSEEVENT),TME_LEAVE,hwnd,HOVER_DEFAULT};
/*static*/ HBRUSH brush = CreateSolidBrush(RGB(20,30,40));
/*static*/ HBRUSH clicked_brush = CreateSolidBrush(RGB(40,50,60));
/*static*/ HBRUSH hover_brush = CreateSolidBrush(RGB(70,80,90));
//TrackMouseEvent(&tme);
switch(msg)
{
case WM_CREATE:
//static_text_handle = CreateWindowW(L"Static",L"test",WS_CHILD | WS_VISIBLE,0,0,20,20,hwnd,NULL,NULL,NULL);
//brush = ((struct custom_button_colors*)(lParam))->default_color;
//clicked_brush = ((struct custom_button_colors*)(lParam))->push_color;
//hover_brush = ((struct custom_button_colors*)(lParam))->hover_color;
break;
case WM_LBUTTONDOWN:
SendMessage(hwnd,WM_ERASEBKGND,(WPARAM)GetDC(hwnd),(LPARAM)LPARAM_L_DOWN);
break;
case WM_LBUTTONUP:
SendMessage(hwnd,WM_ERASEBKGND,(WPARAM)GetDC(hwnd),(LPARAM)LPARAM_L_UP);
break;
case WM_MOUSEMOVE:
//Beep(1000,1000);
TrackMouseEvent(&tme);
if (GetAsyncKeyState(VK_LBUTTON))
{
SendMessage(hwnd,WM_ERASEBKGND,(WPARAM)GetDC(hwnd),(LPARAM)LPARAM_L_DOWN);
}
else
{
SendMessage(hwnd,WM_ERASEBKGND,(WPARAM)GetDC(hwnd),(LPARAM)LPARAM_MOUSEHOVER);
}
break;
case WM_MOUSELEAVE:
SendMessage(hwnd,WM_ERASEBKGND,(WPARAM)GetDC(hwnd),(LPARAM)-1);
break;
case WM_ERASEBKGND:
GetClientRect(hwnd,&rect);
if (lParam == LPARAM_L_DOWN)
{
FillRect((HDC)wParam,&rect,clicked_brush);
}
else if (lParam == LPARAM_MOUSEHOVER)
{
FillRect((HDC)wParam,&rect,hover_brush);
break;
}
else
{
POINT mousepos;
GetCursorPos(&mousepos);
if (WindowFromPoint(mousepos) == hwnd)
{
FillRect((HDC)wParam,&rect,hover_brush);
}
else
{
FillRect((HDC)wParam,&rect,brush);
}
}
hdc = BeginPaint(hwnd,&ps);
color = GetSysColor(COLOR_BTNFACE);
SetBkColor(hdc,color);
font = CreateFontW(25, 0, 0, 0, FW_MEDIUM, 0, 0, 0, 0,0, 0, ANTIALIASED_QUALITY, 0, L"Tahoma");
holdFont = SelectObject(hdc, font);
TextOutW(hdc,200,200,test,lstrlenW(test));
SelectObject(hdc,holdFont);
DeleteObject(font);
EndPaint(hwnd,&ps);
break;
default:
return DefWindowProc(hwnd,msg,wParam,lParam);
}
return 0;
}
I'm an absolute beginner in windows programming so sorry if my question is trivial.
1) Do not use BeginPaint/EndPaint in WM_ERASEBKGND. Move that code in a WM_PAINT handler.
2) Use SetWindowLong and the GWL_USERDATA index for storing the button state (UP or DOWN) and use GetWindowLong for retrieving the current state.
3) Use SetCapture for capturing the mouse, starting with WM_LBUTTONDOWN. Update the state of the button (UP or DOWN) according to mouse position
4) Use RedrawWindow when you need painting to occur