const int jumpHeight = 10;
//Non-Const variables
bool running = true;
bool isJumping = false;
bool isFalling = false;
int jump = 0;
Uint8 *keystate = NULL;
//Structs
typedef struct entity {
SDL_Rect hitbox;
} playerType, enemyType;
playerType player;
enemyType basicEnemy[10];
//Main Function
int main( int argc, char* argv[] )
{
while( running )
{
keystate = SDL_GetKeyState(NULL);
if( keystate[SDLK_UP] )
{
if(isJumping != true)
{
isJumping = true;
}
}
if( keystate[SDLK_LEFT] ) player.hitbox.x -= 1;
if( keystate[SDLK_RIGHT] ) player.hitbox.y += 1;
//Window collision
if( player.hitbox.x < 0 ) {player.hitbox.x = 0;}
else if( player.hitbox.x > SCREEN_WIDTH - player.hitbox.w ) {player.hitbox.x = SCREEN_WIDTH - player.hitbox.w;}
if( player.hitbox.y < 0 ) {player.hitbox.y = 0;}
else if( player.hitbox.y > SCREEN_HEIGHT - player.hitbox.h ) {player.hitbox.y = SCREEN_HEIGHT - player.hitbox.h;}
//Jumping
if( isJumping == true )
{
if( jump >= jumpHeight || isFalling == true )
{
jump--;
player.hitbox.y--;
isFalling = true;
}else if( jump <= 0 && isFalling == true )
{
jump - 0;
isFalling = false;
isJumping = false;
}else {
jump++;
player.hitbox.y++;
}
}
}
}
This is my current code in my game (the parts related to jumping anyway). When I press the Up key my character goes to the top of the window and stays there. Where have I gone wrong?
I compile with g++ -o myprogram.exe mysource.cpp -lmingw32 -lSDLmain -lSDL -lSDL_image -static-libgcc -static-libstdc++.
Walking you through how the code is supposed to work:
Press Up: isJumping becomes true, and then the player is raised to the max height, then goes down again to where it started (later I will add collision checking)
He's going to the top of the window because of this code here:
if (jump >= jumpHeight || isFalling == true) {
jump--;
player.hitbox.y--;
isFalling = true;
}
Once this condition has been met, it will run for the rest of the loop and the conditions will never be false. To fix it, you need to check if jump is 0 after you decrement it. If it's 0, set isFalling to false and isJumping to false.
Also, do note that when you increase the y, you're making the unit go lower on the window because SDL windows have origins starting from the top left corner and the Y axis is downward.
Replace all increments of y with decrements and vice versa.
Related
TL;DR
I am trying to capture keyboard events (more specifically, the Ctrl+c command) in my own C++ program. I am attempting this through generic keyboard presses in SDL2.
END TL;DR
I have found links on SO and the internet that cover the subject of handling keyboard events with SDL2. I have a few of them listed here.
https://stackoverflow.com/questions/28105533/sdl2-joystick-dont-capture-pressed-event
https://lazyfoo.net/tutorials/SDL/04_key_presses/index.php
http://www.cplusplus.com/forum/windows/182214/
http://gigi.nullneuron.net/gigilabs/handling-keyboard-and-mouse-events-in-sdl2/
The major issue I think is causing the problem is that I am also using an Xbox-style joystick at the same time. I have had no issues whatsoever with capturing joystick events. I have been doing that for a long time now. I am having issues trying to get anything with the keyboard to throw an event. I have tried if(event.type == SDL_KEYDOWN) and then checking which key it was, but that appears to return nothing. I feel like there is some macro that I need to define to allow this since I keep finding the same solutions on the internet.
I have included the entire script that I am running at the moment.
#include <boost/thread.hpp>
// Time library
#include <chrono>
// vector data structure
#include <vector>
// Thread-safe base variables
#include <atomic>
// std::cout
#include <iostream>
// Joystick library
#include <SDL2/SDL.h>
// Counters for printing
std::atomic_int printcounter{ 0 };
// This is every 3 * 1000 milliseconds
const int printer = 300;
// If an event is found, allow for printing.
std::atomic_bool eventupdate{ false };
// This function converts the raw joystick axis from the SDL library to proper double precision floating-point values.
double intToDouble(int input)
{
return (double) input / 32767.0 ;
}
// Prevent joystick values from going outside the physical limit
double clamp(double input)
{
return (input < -1.0) ? -1.0 : ( (input > 1.0) ? 1.0 : input);
}
// SDL library joystick deadband
const int JOYSTICK_DEAD_ZONE = 5000;
// These are the raw read in values from the joystick in XInput (XBox) mode.
//Normalized direction
int leftX = 0;
int leftY = 0;
int rightX = 0;
int rightY = 0;
int leftTrigger = -32768;
int rightTrigger = -32768;
// Button array
uint buttons[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// Tbe pov hat is only 4 bits - 1, 2, 4, 8
int povhat = 0;
// These are the rectified joystick values
double leftstickx = 0;
double leftsticky = 0;
double rightstickx = 0;
double rightsticky = 0;
double lefttrigger = 0;
double righttrigger = 0;
// These are the rectified boolean buttons
bool leftstickbut = false;
bool rightstickbut = false;
bool xbutton = false;
bool ybutton = false;
bool abutton = false;
bool bbutton = false;
bool rightbut = false;
bool leftbut = false;
bool startbut = false;
bool backbut = false;
bool centbut = false;
// This is the boolean that controls running the robot.
std::atomic_bool quitrobot{false};
// Joystick values
static double joyvalues[6] = { 0, 0, 0, 0, 0, 0};
static bool joybuttons[11] = { false };
// Sleep function
void wait(int milliseconds)
{
boost::this_thread::sleep_for(boost::chrono::milliseconds{milliseconds});
}
// Now the main code
int main(int argc, char** argv)
{
// Now the robot goes through the looping code until a quit flag is set to true
while ( ! quitrobot)
{
// Now we look for an Xbox-style joystick
std::cout << "Looking for gamepad..." << std::endl;
while(true)
{
// Now the program waits until an Xbox-style joystick is plugged in.
// resetting SDL makes things more stable
SDL_Quit();
// restart SDL with the expectation that a jostick is required.
SDL_Init(SDL_INIT_JOYSTICK);
// SDL_HINT_GRAB_KEYBOARD
// check for a joystick
int res = SDL_NumJoysticks();
if (res > 0) { break; } // Here a joystick has been detected.
if (res < 0)
{
std::cout << "Joystick detection error: " << std::to_string(res) << std::endl;
}
// we don't want the program running super fast when detecting hardware.
wait(20);
}
// Now we check to make sure that the joystick is valid.
// Open the joystick for reading and store its handle in the joy variable
SDL_Joystick *joy = SDL_JoystickOpen(0);
if (joy == NULL) {
/* back to top of while loop */
continue;
}
// Get information about the joystick
const char *name = SDL_JoystickName(joy);
const int num_axes = SDL_JoystickNumAxes(joy);
const int num_buttons = SDL_JoystickNumButtons(joy);
const int num_hats = SDL_JoystickNumHats(joy);
printf("Now reading from joystick '%s' with:\n"
"%d axes\n"
"%d buttons\n"
"%d hats\n\n",
name,
num_axes,
num_buttons,
num_hats);
/* I'm using a logitech F350 wireless in X mode.
If num axis is 4, then gamepad is in D mode, so neutral drive and wait for X mode.
[SAFETY] This means 'D' becomes our robot-disable button.
This can be removed if that's not the goal. */
if (num_axes < 5) {
/* back to top of while loop */
continue;
}
// This is the read joystick and drive robot loop.
while(true)
{
// poll for disconnects or bad things
SDL_Event e;
if (SDL_PollEvent(&e)) {
// SDL generated quit command
if (e.type == SDL_QUIT) { break; }
// Checking for Ctrl+c on the keyboard
// SDL_Keymod modstates = SDL_GetModState();
// if (modstates & KMOD_CTRL)
// {
// One of the Ctrl keys are being held down
// std::cout << "Pressed Ctrl key." << std::endl;
// }
if(e.key.keysym.scancode == SDLK_RCTRL || e.key.keysym.scancode == SDLK_LCTRL || SDL_SCANCODE_RCTRL == e.key.keysym.scancode || e.key.keysym.scancode == SDL_SCANCODE_LCTRL)
{
std::cout << "Pressed QQQQ." << std::endl;
}
if (e.type == SDL_KEYDOWN)
{
switch(e.key.keysym.sym){
case SDLK_UP:
std::cout << "Pressed up." << std::endl;
break;
case SDLK_RCTRL:
std::cout << "Pressed up." << std::endl;
break;
case SDLK_LCTRL:
std::cout << "Pressed up." << std::endl;
break;
}
// Select surfaces based on key press
switch( e.key.keysym.sym )
{
case SDLK_UP:
std::cout << "Pressed Up." << std::endl;
break;
case SDLK_DOWN:
std::cout << "Pressed Up." << std::endl;
break;
case SDLK_LEFT:
std::cout << "Pressed Up." << std::endl;
break;
case SDLK_RIGHT:
std::cout << "Pressed Up." << std::endl;
break;
}
std::cout << "Pressed blah di blah blah please print me." << std::endl;
}
// Checking which joystick event occured
if (e.jdevice.type == SDL_JOYDEVICEREMOVED) { break; }
// Since joystick is not erroring out, we can
else if( e.type == SDL_JOYAXISMOTION )
{
//Motion on controller 0
if( e.jaxis.which == 0 )
{
// event happened
eventupdate = true;
// Left X axis
if( e.jaxis.axis == 0 )
{
// dead zone check
if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
{
leftX = 0;
}
else
{
leftX = e.jaxis.value;
}
}
// Right Y axis
else if( e.jaxis.axis == 1 )
{
// dead zone check
if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
{
leftY = 0;
}
else
{
leftY = e.jaxis.value;
}
}
// Left trigger
else if ( e.jaxis.axis == 2 )
{
// dead zone check
if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
{
leftTrigger = 0;
}
else
{
leftTrigger = e.jaxis.value;
}
}
// Right X axis
else if( e.jaxis.axis == 3 )
{
// dead zone check
if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
{
rightX = 0;
}
else
{
rightX = e.jaxis.value;
}
}
// Right Y axis
else if( e.jaxis.axis == 4 )
{
// dead zone check
if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
{
rightY = 0;
}
else
{
rightY = e.jaxis.value;
}
}
// Right trigger
else if( e.jaxis.axis == 5 )
{
// dead zone check
if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
{
rightTrigger = 0;
}
else
{
rightTrigger = e.jaxis.value;
}
}
}
}
else if ( e.type == SDL_JOYBUTTONUP || e.type == SDL_JOYBUTTONDOWN )
{
// now we are looking for button events.
if (e.jbutton.which == 0)
{
// event happened
eventupdate = true;
// buttons[e.jbutton.button] = e.jbutton.state;
if (e.jbutton.button == 0)
{
buttons[0] = e.jbutton.state;
}
if (e.jbutton.button == 1)
{
buttons[1] = e.jbutton.state;
}
if (e.jbutton.button == 2)
{
buttons[2] = e.jbutton.state;
}
if (e.jbutton.button == 3)
{
buttons[3] = e.jbutton.state;
}
if (e.jbutton.button == 4)
{
buttons[4] = e.jbutton.state;
}
if (e.jbutton.button == 5)
{
buttons[5] = e.jbutton.state;
}
if (e.jbutton.button == 6)
{
buttons[6] = e.jbutton.state;
}
if (e.jbutton.button == 7)
{
buttons[7] = e.jbutton.state;
}
if (e.jbutton.button == 8)
{
buttons[8] = e.jbutton.state;
}
if (e.jbutton.button == 9)
{
buttons[9] = e.jbutton.state;
}
if (e.jbutton.button == 10)
{
buttons[10] = e.jbutton.state;
}
if (e.jbutton.button == 11)
{
buttons[11] = e.jbutton.state;
}
}
}
else if ( e.type == SDL_JOYHATMOTION)
{
if (e.jhat.which == 0)
{
// event happened
eventupdate = true;
povhat = e.jhat.value;
}
}
}
// Now that we have read in the values directly from the joystick we need top convert the values properly.
leftstickx = clamp(intToDouble(leftX));
leftsticky = clamp(intToDouble(leftY));
rightstickx = clamp(intToDouble(rightX));
rightsticky = clamp(intToDouble(rightY));
lefttrigger = clamp(intToDouble(leftTrigger));
righttrigger = clamp(intToDouble(rightTrigger));
// rectify the buttons to become boolean values instead of integers.
abutton = buttons[0] > 0;
bbutton = buttons[1] > 0;
xbutton = buttons[2] > 0;
ybutton = buttons[3] > 0;
//
rightbut = buttons[4] > 0;
leftbut = buttons[5] > 0;
//
centbut = buttons[8] > 0;
startbut = buttons[7] > 0;
backbut = buttons[6] > 0;
//
leftstickbut = buttons[9] > 0;
rightstickbut = buttons[10] > 0;
// Transfer axis to the array.
joyvalues[0] = leftstickx;
joyvalues[1] = leftsticky;
joyvalues[2] = rightstickx;
joyvalues[3] = rightsticky;
joyvalues[4] = lefttrigger;
joyvalues[5] = righttrigger;
// We are using the "B" button to quit the program
if (bbutton)
{
quitrobot = true;
std::cout << "Shutting down program." << std::endl;
break;
}
if (eventupdate)
{
// This section of code is meant for running code that happens when SDL has detected an event.
// This code section can be used for something else as well.
if (e.key.keysym.sym == SDL_SCANCODE_RCTRL || e.key.keysym.sym == SDL_SCANCODE_LCTRL || e.key.keysym.sym == SDLK_LCTRL || e.key.keysym.sym == SDLK_RCTRL)
{
std::cout << "SDL Event: Ctrl pressed.\n" << std::endl;
}
// Simply print the event
eventupdate = false;
} else {}
if ( ! (printcounter = ((printcounter + 1) % printer)))
{
// const Uint8 *state = SDL_GetKeyboardState(NULL);
// if (state[SDL_SCANCODE_RETURN]) {
// printf("<RETURN> is pressed.\n");
// }
}
// Sleep the program for a short bit
wait(5);
}
// Reset SDL since the robot is no longer being actuated.
SDL_JoystickClose(joy);
// We get here only if the joystick has been disconnected.
std::cout << "Gamepad disconnected.\n" << std::endl;
}
// The program then completes.
return 0;
}
The most important part of that huge block of code is lines 129 to 179. I was doing more fooling around trying to get key capture to work but I could not get a response. Everywhere else is logic for the joystick reading (which has worked for me flawlessly). I have been referring to this link for all of the macros available to the programmer. I have not been able to capture the left control button or the right control button. I have also been trying the arrow keys for kicks as well and those are not working either. I know there are remnants of other code snippets thrown in there as I was testing. Given all of my testing, I am just not sure how to capture any keyboard keys, let alone Ctrl+c. None of my desired print statements print.
I am able to run the code on Ubuntu 1804 LTS with the stock GUI manager and window manager. I have a feeling the problem might also have something to do with the operating system not letting SDL2 capture the keyboard, but I don't know what to do to allow only the keyboard or certain keys to be consumed by SDL2.
I am trying to not use platform-specific code since I already have successfully used platform-specific signal interrupts. My goal is to simply make a certain combination of depressed keys result in a program terminating. I figured that, since SDL2 can access all keys on a keyboard, that I should be able to simply
Unless you want to read keyboard input from stdin you need to open a window and focus it to get key events in SDL. Here's an example (note the call to SDL_Init uses SDL_INIT_VIDEO and there's some code in there for rendering a background and handling resize events).
#include <iostream>
#include <SDL2/SDL.h>
int main(int argc, char** argv)
{
if (SDL_Init(SDL_INIT_VIDEO) < 0) { // also initialises the events subsystem
std::cout << "Failed to init SDL.\n";
return -1;
}
SDL_Window *window = SDL_CreateWindow(
"Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
680, 480, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if(!window) {
std::cout << "Failed to create window.\n";
return -1;
}
// Create renderer and select the color for drawing.
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255);
while(true)
{
// Clear the entire screen and present.
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
SDL_Event event;
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT) {
SDL_Quit();
return 0;
}
if(event.type == SDL_WINDOWEVENT) {
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
int width = event.window.data1;
int height = event.window.data2;
std::cout << "resize event: " << width << "," << height << std::endl;
}
}
if (event.type == SDL_KEYDOWN) {
int key = event.key.keysym.sym;
if (key == SDLK_ESCAPE) {
SDL_Quit();
return 0;
}
std::cout << "key event: " << key << std::endl;
}
}
}
return 0;
}
Key events are sent to the currently focused window and SDL uses the underlying OS to handle this. E.g. In Linux this means SDL calls X11 functions.
edit:
As detailed in this question it appears you can also get a snapshot of the state of the keys. In either case I think a window needs to be opened to receive events even though I've edited this multiple times to come to that conclusion (apologies if that caused any confusion). Your OS may provide functions for polling the state of the keyboard without using events or windows, such as GetAsyncKeyState in Windows.
I am trying to make a custom app library with SDL as apart of an academic project, and I ran into an issue..
Basically, everything works fine, it compiles, does what I expect it to do, but... its extremely slow, the first element is reacting quite quickly, other elements within the set are completely unresponsive (i need to click them 20 times for the to react, they work just slow)
Below the function that draws the elements from a vector type container, the return 1 means that the handle function ran into an unexpected error or the user X'ed out the window.
Any advice on how to make this react faster?
void StoreDraw::setCamera(size_t input)
{
rectangle.x = containerArray[input].elementLocX;
rectangle.y = containerArray[input].elementLocY;
rectangle.h = containerArray[input].elementPicture->h;
rectangle.w = containerArray[input].elementPicture->w;
}
bool StoreDraw::vectorDraw()
{
/* Draw FloatingElements */
for(size_t i = 0; i < containerArray.size(); i++)
{
if(SDL_PollEvent(&event))//containerArray[i].event
{
if(event.type == SDL_MOUSEBUTTONDOWN)
{
if(event.button.button == SDL_BUTTON_LEFT)
{
locationX = event.button.x;
locationY = event.button.y;
printf("X:%d\tY:%d\n", locationX, locationY);
if(!containerArray[i].handleEvent(locationX, locationY)){drawEnvironment();}
}
}
if(event.type == SDL_QUIT)
{
return 1;
}
}
}
SDL_Flip(screen);
return 0;
}
bool StoreDraw::drawEnvironment()
{
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0,0,0));
SDL_BlitSurface(background, NULL, screen, NULL);
for(size_t i = 0; i < containerArray.size(); i++)
{
setCamera(i);
SDL_BlitSurface(containerArray[i].elementPicture, NULL, screen, &rectangle);
}
SDL_Flip(screen);
}
bool FloatingElement::handleEvent(int x, int y)
{
printf("Object responding.\n");
if((x > elementLocX) && (x < (elementLocX + (elementPicture->w))) && (y > elementLocY) && (y < (elementLocY + (elementPicture->h))))
{
x = (x - (elementPicture->w)/2);
y = (y - (elementPicture->h)/2);
setLocation(x,y);
printf("Object responding.\n");
return 0;
}
return 1;
}
As far as I can see, the problem is how frequently you are checking for events. When you call vectorDraw() it polls the events and acts as intended, but the events eventually end and the function returns. I think there should be a main loop in the code that always checks for events and calls functions when necessary, while vectorDraw() only checks the location of a click and calls the handleEvent() of the container array.
Edit:
I think I found the problem. This is how you're doing now:
bool StoreDraw::vectorDraw()
{
/* Draw FloatingElements */
for(size_t i = 0; i < containerArray.size(); i++)
{
if(SDL_PollEvent(&event))//containerArray[i].event
{
if(event.type == SDL_MOUSEBUTTONDOWN)
{
if(event.button.button == SDL_BUTTON_LEFT)
{
// Check Events
}
}
if(event.type == SDL_QUIT)
{
return 1;
}
}
}
SDL_Flip(screen);
return 0;
}
int main() {
while( ::quit == false ) {
while(SDL_PollEvent(&event) == 1) {
if( event.type != SDL_QUIT ) {
if( event.type == SDL_MOUSEBUTTONDOWN) {
storeDraw::vectorDraw();
}
} else {
quit = true;
break;
}
}
}
}
}
So, what is happening:
After the main loop polls the event, it calls vectorDraw() and vectorDraw() polls events again. By doing so, vectorDraw() doesn't get the information of the click it called, as it was already polled in the main loop. Without the information, it doesn't act upon it.
So, in order to solve the problem, you can change the functions to be somewhat like this:
bool StoreDraw::vectorDraw(SDL_Event event)
{
/* Draw FloatingElements */
for(size_t i = 0; i < containerArray.size(); i++)
{
// Check Events
}
SDL_Flip(screen);
return 0;
}
int main() {
while( ::quit == false ) {
while(SDL_PollEvent(&event) == 1) {
if( event.type != SDL_QUIT ) {
if( event.type == SDL_MOUSEBUTTONDOWN) {
if(event.button.button == SDL_BUTTON_LEFT)
{
storeDraw::vectorDraw(event);
}
}
} else {
quit = true;
break;
}
}
}
}
}
Now, only the main loop polls events and acts on them if possible. And vectorDraw() doesn't poll them anymore, only acts upon them if they meet it's criteria (left mouse button down, in this case). Now the event checking should act as intended.
An snippet of my code from main.cpp
playerEntity::handle()
{
if( event.type == SDL_KEYDOWN )
{
switch( event.key.keysym.sym )
{
case SDLK_q:
running = false;
paused = true;
break;
case SDLK_ESCAPE:
paused = !paused;
break;
}
}
if( keystate[SDLK_UP] )
{
if( isJumping == false && isFreeFalling == false )
{
isJumping = true;
}
}
if( keystate[SDLK_LEFT] ) player.hitbox.x--;
if( keystate[SDLK_RIGHT] ) player.hitbox.x++;
if( player.hitbox.x < 0 ) {player.hitbox.x = 0;}
else if( player.hitbox.x > screen.WIDTH - player.hitbox.w ) {player.hitbox.x = screen.WIDTH - player.hitbox.w;}
if( player.hitbox.y < 0 ) {player.hitbox.y = 0;}
else if( player.hitbox.y > screen.HEIGHT - player.hitbox.h ) {player.hitbox.y = screen.HEIGHT - player.hitbox.h;}
}
Where playerEntity is defined in a header file:
#ifndef PLAYERENTITY_H
#define PLAYERENTITY_H
class playerEntity
{
private:
int jumpHeight;
int jump;
bool isJumping;
bool isFalling;
bool isFreeFalling;
SDL_Event event;
Uint8 *keystate;
public:
playerEntity();
void jump();
void handle();
void fall();
int health;
int damage;
SDL_Rect hitbox;
bool evolved;
};
#endif
And when I try to compile I get the errors:
ISO c++ forbids declaration of 'handle' with no type [-fpermissive]
prototype for 'int playerEntity::handle()' does not match any in class 'playerEntity'
error: candidate is: void playerEntity::handle().
I am still new to header files and classes, how do I fix the errors?
you should replace
playerEntity::handle()
with
void playerEntity::handle()
Write
void playerEntity::handle()
C++ requires the return type (which in this case is the nontype void) to be mentioned in the function's definition—an important type-safety measure.
By the way, you should probably move the definition of playerEntity::handle() from main.cpp to a new file, playerEntity.cpp. Other files are possible, too, but few good programmers would leave this definition in main.cpp. Unfortunately—well, actually fortunately—this will put you through several hours of the urgently necessary pain of learning separate compilation and linking.
Good luck.
I created a TrayPopupWidget that should pops up nearby the tray.
Then I realized that if the user changes the orientation, or height of the taskbar it will pops up at the wrong place. So I created the TaskbarDetector class.
I tried to get the a window geometry of the tray|taskbar, but I only get wrong window property...
I tried on KDE,LXDE -> same bad behaviour...
The code:
http://bzfriendsplasm.svn.sourceforge.net/viewvc/bzfriendsplasm/BZFriends/taskbardetector.cpp?revision=156&view=markup
//Getting screen resolutoin
int num_sizes;
Rotation original_rotation;
Display *display = XOpenDisplay(NULL);
Window root = RootWindow(display, 0);
XRRScreenSize *xrrs = XRRSizes(display, 0, &num_sizes);
XRRScreenConfiguration *conf = XRRGetScreenInfo(display, root);
XRRConfigCurrentRate(conf);
SizeID original_size_id = XRRConfigCurrentConfiguration(conf, &original_rotation);
p_screenWidth = xrrs[original_size_id].width;
p_screenHeight = xrrs[original_size_id].height;
//Getting tray position
unsigned long sysTraySelection = 0;
Screen *screen = XDefaultScreenOfDisplay(display);
//FIXME !!!
QString *net_sys_tray = new QString("_NET_SYSTEM_TRAY_S%i");
(*net_sys_tray) = net_sys_tray->replace ("%i",QString::number (XScreenNumberOfScreen(screen)));
sysTraySelection = XInternAtom(display, net_sys_tray->toLocal8Bit (), False);
if ( sysTraySelection == None)
return Unknown;
trayWindow = XGetSelectionOwner(display, sysTraySelection);
XWindowAttributes w_attr;
unsigned long status = XGetWindowAttributes (display,trayWindow,&w_attr);
if ( status == 0)
return Unknown;
p_taskBarLeft = w_attr.y;
p_taskBarTop = w_attr.x;
p_taskBarBottom = w_attr.x + w_attr.height;
p_taskBarRight = w_attr.y + w_attr.width;
qDebug () << QString("Window id: " ) + QString::number (trayWindow);
qDebug() << QString("SysTraySelection: ") + QString::number (sysTraySelection );
qDebug() << QString("Top ") + QString::number (p_taskBarTop);
qDebug() << QString("Left ") + QString::number (p_taskBarLeft);
qDebug() << QString("Bottom ") + QString::number (p_taskBarBottom);
qDebug() << QString("Right " ) + QString::number (p_taskBarRight);
XCloseDisplay(display);
delete net_sys_tray;
return decideOrientation ();
Finally, I found a way to detect it!
I'm searching for a window, that has a dock property and visible, and then I call XGetWindowAttributes(...) on it.
If I set a filter method to the QApplication::setEventFilter() I can get every XEvent, _NET_WORKAREA event too(this event happens when you resize or move the taskbar), then I re-call taskbar/tray detection method.
However, this worked on KDE4 and GNOME and LXDE, I'm planning to allow the user to set the popup position by himself.
bool TaskBarDetector::lookUpDockWindow ( unsigned long &rootWindow, bool check)
{
Display *display = QX11Info::display ();
Window parent;
Window *children;
unsigned int noOfChildren;
int status;
if ( check && checkDockProperty(rootWindow) )
{
trayWindow = rootWindow;
return true;
}
status = XQueryTree (display, rootWindow, &rootWindow, &parent, &children, &noOfChildren);
if (status == 0)
{
qDebug() << "ERROR - Could not query the window tree. Aborting.";
trayWindow = 0;
return false;
}
if (noOfChildren == 0)
{
trayWindow = 0;
return false;
}
for (unsigned int ind = 0 ; ind < noOfChildren; ++ind )
{
if ( lookUpDockWindow ( children[ind] ,true) )
return true;
}
XFree ((char*) children);
trayWindow = 0;
return false;
}
bool TaskBarDetector::checkDockProperty(unsigned long window)
{
Display *x11display = QX11Info::display ();
Atom *atoms;
int numberAtoms = 0;
char *atomName;
XTextProperty prop;
XWindowAttributes windowattr;
atoms = XListProperties (x11display, window, &numberAtoms);
for (int ind = 0; ind < numberAtoms; ++ind )
{
atomName = XGetAtomName(x11display, atoms[ind]);
if (QString(atomName).compare ("_NET_WM_WINDOW_TYPE" ) != 0 )
continue;
unsigned long status = XGetTextProperty (x11display,window,&prop,atoms[ind]);
if ( status == 0 )
continue;
int value = (int) (*prop.value);
if (value != 151 )
continue;
if (XGetWindowAttributes(x11display,window,&windowattr) == 0)
continue;
return windowattr.map_state == 2;
}
return false;
}
void TaskBarDetector::saveWindowAttr(unsigned long root)
{
XWindowAttributes windowattr;
Display *x11display =QX11Info::display ();
if (XGetWindowAttributes(x11display,trayWindow,&windowattr) == 0)
{
trayWindow = 0;
return;
}
int x = 0;
int y = 0;
Window *w = &trayWindow;
if( XTranslateCoordinates(x11display,trayWindow,root,windowattr.x,windowattr.y,&x,&y,w) == True)
{
p_taskBarTop = y;
p_taskBarLeft = x;
p_taskBarRight = p_taskBarLeft + windowattr.width;
p_taskBarBottom = p_taskBarTop + windowattr.height;
p_taskBarHeight = windowattr.height;
p_taskBarWidth = windowattr.width;
} else
{
p_orientation = Unknown;
p_taskBarTop = 0;
p_taskBarLeft = 0;
p_taskBarRight = 0;
p_taskBarBottom = 0;
p_taskBarHeight = 0;
p_taskBarWidth = 0;
}
bool TaskBarDetector::appEventFilter(void *msg, long *result)
{
Q_UNUSED(result);
if ( !TaskBarDetector::hasInstance() )
return false;
TaskBarDetector *detector = TaskBarDetector::getInstance();
#ifdef Q_WS_WIN
MSG *seged = static_cast<MSG*>(msg);
if ( seged->message == WM_SETTINGCHANGE && seged->wParam == SPI_SETWORKAREA )
{
detector->processDetectEvent();
return false;
}
return false;
#endif
#ifdef Q_WS_X11
XEvent *xevent = static_cast<XEvent*> (msg);
if ( xevent->type == PropertyNotify )
{
XPropertyEvent xpe = xevent->xproperty;
char * ch_atom_name = XGetAtomName(QX11Info::display(),xpe.atom);
QString atom_name = QString(ch_atom_name).trimmed ();
if ( atom_name == "_NET_WORKAREA" )
{
detector->processDetectEvent ();
return false;
}
}
return false;
#endif
}
}
The system tray is not necessarily a window per se. In KDE, this is just an area in the taskbar (and it is unrelated to the _NET_SYSTEM_TRAY_S%i selection owner).
You may want to try embedding a tray icon and getting its geometry, then display your widget "near" said icon (for some reasonable value of "near"). You can delete the icon once you know its geometry (but then if the user moves the tray, you won't know its new coordinates).
This is not 100% reliable, as the tray is in no way obliged to show all icons you can throw at it. Also, not visually pleasing because of the icon flicker. But it's better than nothing.
this is regarding this tutorial page from lazyfoo's set of SDL tutorials. There he first starts a timer to calculate how much time each frame should stay alive for, before it is refreshed. He does this using the following
if( ( cap == true ) && ( fps.get_ticks() < 1000 / FRAMES_PER_SECOND ) ) {
//Sleep the remaining frame time
SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - fps.get_ticks() );
}
Although I've found that fps.get_ticks() always returns 0 (??) and so isn't the above not needed(?), cant we just completely leave out the timer and just delay for 1000/FPS.
I've tried both ways below and both give me the same thing. What am I missing here, why do we need a timer?.
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include <iostream>
SDL_Surface *background = NULL;
SDL_Surface *screen = NULL;
SDL_Surface *msg = NULL;
const int FPS = 20;
void initialize(void){
if (SDL_Init(SDL_INIT_EVERYTHING) == -1 ){
std::cout<<"could not start sdl"<<std::endl;
}
screen = SDL_SetVideoMode(640,480,32,SDL_SWSURFACE);
if (screen == NULL){
std::cout<<"could not make screen"<<std::endl;
}
}
void cleanUp(void){
SDL_Quit();
SDL_FreeSurface(background);
SDL_FreeSurface(msg);
}
void loadFiles(void){
background = IMG_Load("background.bmp");
msg = IMG_Load("msg.bmp");
if (background == NULL){
std::cout<<"could not load background"<<std::endl;
}
if (msg == NULL){
std::cout<<"could not load msg"<<std::endl;
}
}
void blitSurf(int x,int y,SDL_Surface *source,SDL_Surface *dest){
SDL_Rect dest_pos;
dest_pos.x = x;
dest_pos.y = y;
if (SDL_BlitSurface(source,NULL,dest,&dest_pos) == -1){
std::cout<<"could not blit surface"<<std::endl;
}
}
void update(void){
if (SDL_Flip(screen) == -1 ){
std::cout<<"could not update screen"<<std::endl;
}
}
int main(int argc,char *argv[]){
initialize();
loadFiles();
bool running = true;
bool cap = false;
int msg_pos_y = 0;
int start = 0;
int temp = 0;
SDL_Event event;
while (running == true){
start = SDL_GetTicks();
while (SDL_PollEvent(&event)){
if (event.type == SDL_KEYDOWN){
if (event.key.keysym.sym == SDLK_c){
if(cap == false){
cap = true;
std::cout<<"cap set to, true"<<std::endl;
}else{
cap = false;
std::cout<<"cap set to, false"<<std::endl;
}
}
}
if (event.type == SDL_QUIT){
running = false;
std::cout<<"Quit was pressed"<<std::endl;
}
}
blitSurf(0,0,background,screen);
if (msg_pos_y < 640){
blitSurf(200,msg_pos_y,msg,screen);
msg_pos_y++;
}else{
msg_pos_y = 0;
blitSurf(200,msg_pos_y,msg,screen);
}
update();
if ( (cap == true) && ( (SDL_GetTicks()-start) < (1000/FPS) ) ){
SDL_Delay( (1000/FPS) - (SDL_GetTicks()-start) );
}
/* this works as well ??
if ( cap == true ){
SDL_Delay(1000/FPS);
}
*/
}
cleanUp();
return 0;
}
Let's say you want 50 fps.
1000miliseconds / 50 = 20miliseconds delay.
But it takes time to render, compute physics, AI, whatever you are doing. Let's say that all this stuff I wrote takes 10miliseconds. You have 1000 miliseconds / (20ms delay + 10ms everything else) = 33.3 frames per second. You need to substract this 10ms from the delay.
I'm no expert but what he does is : once your main loop that runs your sdl app is finished (drawing blitting and all that stuff), time may have passed a bit while you were doing your stuff, for example if it took 10ms to do draw your frame, you have to wait (1000/FPS) - 10 ms until the next frame, otherwise your frame will lasts for too long.