How can I simulate a drag action using Xlib? - c++

I have an application which uses Xlib library for simulate a mouse on the screen.
I used XQueryPointer and XWarpPointer functions with which I can simulate a mouse movement and also a click. My problem is that I can't "drag" an element (it's an interface with some blocks which you can move within a Qt application written in C++ and launched on Ubuntu).
I use many parts of this extract:
Sending X11 click event doesn't work with some windows
Can this function help me?
::XGrabPointer(mDisplay, window, True,
ButtonPressMask |
ButtonReleaseMask |
PointerMotionMask |
FocusChangeMask |
EnterWindowMask |
LeaveWindowMask,
GrabModeAsync,
GrabModeAsync,
RootWindow(mDisplay, DefaultScreen(mDisplay)),
None,
CurrentTime);
Do you have any idea?
Thank you in advance.

If you're okay with spawning a child process, you can easily use xdotool for this, as in:
xdotool mousedown 1
sleep 0.5
xdotool mousemove_relative --sync 200 200
sleep 0.5
xdotool mouseup 1
http://www.semicomplete.com/projects/xdotool/
http://tuxradar.com/content/xdotool-script-your-mouse
The source code to xdotool would serve as a good starting point if you want to do this yourself in C with the XTest library.

You can achieve this by sending the XMotionEvent to the window. the way X11 know that it is a drag event is by checking the state. So, if the state is OR with the Button1Mask then it means that the Motion is done when the Button1 was pressed.
I am pasting the pseudo code here. You might want to populate the relevant fields of the XMotionEvent as per your use-case.
Display *OpenedDisplay = XOpenDisplay(NULL);
int ret = 0,screen = 0;
XMotionEvent xbpe;
Window dummy;
unsigned int mask,dummyUInt;
int dummyInt;
int screencount = ScreenCount(xbpe.display);
for (int i = 0; i < screencount; i++) {
Screen *screenPointer = ScreenOfDisplay(xbpe.display, i);
ret = XQueryPointer(xbpe.display, RootWindowOfScreen(screenPointer), &dummy, &dummy,
&dummyInt, &dummyInt, &dummyInt, &dummyInt, &mask);
if (ret == True) {
screen = i;
break;
}
}
xbpe.type = MotionNotify;
xbpe.display = OpenedDisplay;
xbpe.window = wid;
xbpe.root = RootWindow(xbpe.display, screen);
xbpe.subwindow = None;
xbpe.time = CurrentTime;
xbpe.x = src_x;
xbpe.y = src_y;
xbpe.state = mask;
xbpe.is_hint = NotifyNormal;
xbpe.same_screen = True; /* Should we detect if window is on the same screen as cursor? */
XWindowAttributes attr;
ret = XGetWindowAttributes(xbpe.display, wid, &attr);
if(ret == 0){
qDebug() << wid <<"XGetWindowAttributes unsuccessfull returning early";
return;
}
/**************************** This is key here *******************/
if(isDrag){
xbpe.state |= Button1Mask;
}
ret = XSendEvent(xbpe.display, wid, False, PointerMotionMask|ButtonMotionMask|MotionNotify, (XEvent *)&xbpe);
XFlush(OpenedDisplay);
XCloseDisplay(OpenedDisplay);
I would recommend navigating the source code of xdotool. It doesn't specifically covers your issue but gives you enough information on populating the fields of X*Event.

Related

Context menu does not consistently work on arch linux?

I am using arch linux and a basic cpp xlib custom window manager. However, every time I right click to open the context menu it just flickers and disappears. I cannot use it at all. I also cannot use top drop down menus (file, edit, about, ect.) on any application. Is there anything in Xlib which I have to look out for to ensure I may use the context menus normally?
This is the case in every application I have tried. Only clue I have is in brave it occasionally displays the following message:
XGetWindowAttributes failed for window [WINDOW_ID]
The following simplified example also has this issue:
int main()
{
display = XOpenDisplay(nullptr);
root = DefaultRootWindow(display);
XSelectInput(display, root, SubstructureRedirectMask | SubstructureNotifyMask | StructureNotifyMask);
XGrabServer(display);
Window returned_root;
Window returned_parent;
Window* top_level_windows;
unsigned int num_top_level_windows;
XQueryTree(display, root, &returned_root, &returned_parent, &top_level_windows, &num_top_level_windows);
for(unsigned int i = 0; i < num_top_level_windows; ++i)
{
Frame(top_level_windows[i], true);
}
XFree(top_level_windows);
XUngrabServer(display);
for(;;)
{
XEvent event;
XNextEvent(display, &event);
switch (event.type)
{
case MapRequest:
{
Frame(event.xmaprequest.window, false);
XMapWindow(display, event.xmaprequest.window);
break;
}
case ButtonPress:
XRaiseWindow(display, event.xbutton.window);
break;
}
}
return true;
}
void Frame(Window window, bool created_before_manager)
{
//Retrieve attributes of window to frame
XWindowAttributes attr = {0};
XGetWindowAttributes(display, window, &attr);
//If window was created before window manager started, we should frame it only if it is visible and does not set override_redirect
if(created_before_manager && (attr.override_redirect || attr.map_state != IsViewable))
{
return;
}
//Create frame
Window frame = XCreateSimpleWindow(display, root, attr.x, attr.y, attr.width, attr.height, 5, 0xff0000, 0xffffff);
XReparentWindow(display, window, frame, 0, 0);
XMapWindow(display, frame);
XGrabButton(display, Button1Mask, Mod1Mask, window, None, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
}
To be clear it also works with a super simple example such as:
int main()
{
Display* display = XOpenDisplay(nullptr);
for(;;) {}
return true;
}
The reason I believe the window manager is at fault is because this issue only occurs after I run the window manager.
I expected this to work out of the box. I have not found any information on context menus needing special treatment. They do have the override_redirect flag set to true, so I do not frame them. I cannot find information on any other special treatment required.
It is necessary to make sure the client window has input. I had the input set to whatever was clicked (frame, title bar, or client) because it worked fine as far as normal input is concerned. However, the context menus will only work if you make sure the input is set to the client window directly.

MFC floating toolbar always active

I' m new with MFC. I needed to create a floating toolbar (CToolBar) with no option of docking and save and restore its last pos.
The toolbar also should be active all the time, but its NOT.
When I'm openning a new child window (dialog for instance) from the mainframe, the floating tool bar become not active (I can not click on its buttons, or drag it etc..).
In the past I've used CDiaolog with Overlapped style and it was floating and always active as I needed. Is it possible to do the same with my Floating Toolbar? Thanks
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
toolbarIconSize.cx = toolbarIconSize.cy = TOOLBAR_MAIN_ICON_SIZE;
if ( !m_wndMyFloatingToolbar.Create(this,m_wndMyFloatingToolbar.GetBarStyle() |WS_EX_PALETTEWINDOW | WS_EX_TOPMOST |CBRS_FLOATING | WS_VISIBLE) ||
!m_wndMyFloatingToolbar.LoadToolBar(IDR_GENERAL_TOOLBAR, toolbarIconSize))
{
TRACE0("Failed to create My Floating Toolbar\n");
return -1; // fail to create
}
m_wndMyFloatingToolbar.EnableDocking(0);
EnableDocking(0);
if (!CreateCtrlBar())
{
TRACE0("Failed to create ctrl toolbar\n");
return -1; // fail to create
}
// ...
//...
return 0;
}
void CMainFrame::OnViewToolBar()
{
// ...
//...
CPoint Pos = MyFloatingToolbarGetLastPosition(); \\Get last pos
FloatControlBar( &m_wndMyFloatingToolbar, Pos, CBRS_ALIGN_LEFT );
MyFloatingToolbarSetIsVisible();
FloatControlBar( &m_wndMyFloatingToolbar, Pos, CBRS_ALIGN_LEFT );
}
void CMainFrame::MyFloatingToolbarSetIsVisible()
{
WINDOWPLACEMENT wp;
m_wndMyFloatingToolbar.GetParent()->GetParent()->GetWindowPlacement(&wp);
wp.showCmd = SW_SHOW;
m_wndMyFloatingToolbar.GetParent()->GetParent()->SetWindowPlacement(&wp);
m_wndMyFloatingToolbar.GetParent()->GetWindowPlacement(&wp);
wp.showCmd = SW_SHOW;
m_wndMyFloatingToolbar.GetParent()->SetWindowPlacement(&wp);
m_wndMyFloatingToolbar.GetWindowPlacement(&wp);
wp.showCmd = SW_SHOW;
m_wndMyFloatingToolbar.SetWindowPlacement(&wp);
}
void CWJToolBar::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos)
{
CToolBar::OnWindowPosChanging(lpwndpos);
if ( GetBarStyle() & CBRS_FLOATING )
{
if((lpwndpos->flags & SWP_HIDEWINDOW) && ((this->GetParentFrame())->m_hWnd !=(this->GetTopLevelFrame())->m_hWnd))
{
CMainFrame* mf = (CMainFrame*)(AfxGetApp()->GetMainWnd());
mf->MyFloatingToolbarSavePosition();
}
}
}
You may need to debug to view its coordinates if they are correctly set. Be independent. :p
Based on your current posted code, I don't see the point of your stored data, try this
hiding your toolbar
saving its position data
changing your parent windows position and
reloading your saved coordinates.
The saved data becomes incorrect values then.
I suggest you capture the position to which you want to add your toolbar live . This makes your toolbar application more generic.
So,
Save your toolbar's i.e top-left distance to its parent windows, not its coordinates
Get your parent windows coordinates
Reload your toolbar based on the saved distance
There are of course other ways to do this but I think this is more trivial to accomplish what you may be looking for.
Use CMFCToolBar (instead CToolBar), then you need only 2 commands, to achieve this.
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CMDIFrameWndEx::OnCreate(lpCreateStruct) == -1)
return -1;
:
m_wndToolBar.SetPermament(TRUE); // it removes CloseButton (=always active)
CRect rect;
GetClientRect(&rect);
ClientToScreen(rect);
rect.OffsetRect(100, 20);
m_wndToolBar.FloatPane(rect); // Float and move it to your wished coordinates
:
}

How can you efficiently create an allegro 5 title menu?

I'm working on my first game in Allegro 5, I've got the title menu rendering as such, however I want to add clickable text in the menu. How would I make it so that, when you hover over the text you can click it? I'm thinking having a for statement checking the pixels would be very bad for performance, here's what I have so far:
#include <allegro5\allegro.h>
#include <allegro5\allegro_image.h>
#include <allegro5\allegro_primitives.h>
const int width = 1280;
const int height = 720;
int main(void)
{
al_init();
al_init_primitives_addon();
al_init_image_addon();
ALLEGRO_DISPLAY *display = al_create_display(width, height);
ALLEGRO_BITMAP *title = al_load_bitmap("titlemenu.bmp");
al_clear_to_color(al_map_rgb(0, 0, 0));
al_draw_bitmap(title, 0, 0, 0);
al_flip_display();
al_rest(3.0);
al_destroy_display(display);
return 0;
}
I'm using codeblocks on windows XP SP3
To do it "properly," you'd need to use some sort of GUI library. But you can easily create a clickable section of the screen by hardcoding some rectangle's coordinates.
First you'll need to set up your event handling:
ALLEGRO_EVENT_QUEUE *queue;
queue = al_create_event_queue();
al_install_keyboard();
al_register_event_source(queue, al_get_keyboard_event_source());
Without getting into the specifics of event handling (it's an entire topic of its own), here's the relevant bit:
int selection = 0;
while (!selection)
{
ALLEGRO_EVENT event;
al_wait_for_event(queue, &event);
if (event.type == ALLEGRO_EVENT_KEY_UP)
{
if (event.keyboard.keycode == ALLEGRO_KEY_ESCAPE)
selection = MYGAME_QUIT;
}
else if (event.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP)
{
if (event.mouse.x >= MYGAME_MENU_X1 && event.mouse.x < MYGAME_MENU_X2 &&
event.mouse.y >= MYGAME_MENU_Y1 && event.mouse.y < MYGAME_MENU_Y2)
{
selection = MYGAME_OPTION1;
}
}
}
There are many ways to improve upon this example... This is just to get you started.
You should carefully read through the documentation regarding event handling and examine the bundled examples and check out the wiki for more information.
PS: Use forward slashes when using file paths, as they are cross platform:
#include <allegro5/allegro.h>
The above isn't true;
You can add the keyboard to your game and then you have 2 options for collision detection, pixel perfect and bounding box, both covered in the allegro 5 wiki. The tick here to create the best menu IMO is to create a loop and timer for the menu, then list the keyboard, mouse, and timer events seperatley. Next make some if statements so the mouse or keyboard event only triggers when you actually click it, this is needed so you can scroll through a menu with both mouse and keypad, or you could just make it so the mouse wont affect the screen until after you click, but former looks better, at least IMO.

Getting mouse events from HIDManager in OSX with C++

I'm porting a game from pc to osx and I'm getting stuck with input events. The main game window is a cocoa app that interfaces with a C++ backend. The pc code uses DirectInput and windows messages to generate keyboard and mouse events that the game understands.
When I first started the port I replaced the windows messages with Carbon event handling, but since discovered that Cocoa apps don't fire off carbon events. I did a bit more reading and discovered the HIDManager which seemed to do what I want and was accessible from c++. Using the information in the post here Using IOHIDManager to Get Modifier Key Events I managed to get keyboard input working, but have so far been unable to extend the example code to generate mouse events as well. The code is have is as follows:
void myHIDCallback(void* context, IOReturn result, void* sender, IOHIDValueRef value)
{
IOHIDElementRef elem = IOHIDValueGetElement(value);
if (IOHIDElementGetUsagePage(elem) == 0x07)
{
// Keyboard events
card32 scancode = IOHIDElementGetUsage(elem);
if (scancode >= 4 && scancode <= 231)
{
long pressed = IOHIDValueGetIntegerValue(value);
KEY_EVENT_DETAILS details = { KEY_EVENT_NONE, NUM_KEYS };
for (card32 n=0; n<NUM_KEYS; ++n)
{
if (n_direct_input_mappings[n].direct_input_key == scancode)
{
details.key_type = n_direct_input_mappings[n].key;
break;
}
}
switch (pressed)
{
case 0:
details.event_type = KEY_EVENT_KEY_DOWN;
break;
case 1:
details.event_type = KEY_EVENT_KEY_UP;
break;
}
sApplication->handle_key_event(details);
}
}
else
if (IOHIDElementGetUsagePage(elem) == 0x02)
{
// Mouse events
card32 usage = IOHIDElementGetUsage(elem);
long pressed = IOHIDValueGetIntegerValue(value);
}
else
{
}
}
CFMutableDictionaryRef myCreateDeviceMatchingDictionary(UInt32 usagePage, UInt32 usage)
{
CFMutableDictionaryRef ret = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (ret)
{
CFNumberRef pageNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usagePage);
if (pageNumberRef)
{
CFDictionarySetValue(ret, CFSTR(kIOHIDDeviceUsagePageKey), pageNumberRef);
CFRelease(pageNumberRef);
CFNumberRef usageNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
if (usageNumberRef)
{
CFDictionarySetValue(ret, CFSTR(kIOHIDDeviceUsageKey), usageNumberRef);
CFRelease(usageNumberRef);
return ret;
}
}
CFRelease(ret);
}
return NULL;
}
bool acquire_hardware_controllers(CA::Application& app, CAWindow& window_handle)
{
sApplication = &app;
// Setup the keyboard event handler
sHIDManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
IOHIDManagerOpen(sHIDManager, kIOHIDOptionsTypeNone);
CFMutableDictionaryRef mouse = myCreateDeviceMatchingDictionary(0x01, 2);
CFMutableDictionaryRef keyboard = myCreateDeviceMatchingDictionary(0x01, 6);
CFMutableDictionaryRef keypad = myCreateDeviceMatchingDictionary(0x01, 7);
CFMutableDictionaryRef matchesList[] = {
keyboard,
keypad,
mouse
};
CFArrayRef matches = CFArrayCreate(kCFAllocatorDefault, (const void**)matchesList, 3, NULL);
IOHIDManagerSetDeviceMatchingMultiple(sHIDManager, matches);
IOHIDManagerRegisterInputValueCallback(sHIDManager, myHIDCallback, NULL);
IOHIDManagerScheduleWithRunLoop(sHIDManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
}
If anyone can help me out then that would be great. If I can't get this working then I guess I can move over to Cocoa event handling, but the pc code also has some keyboard and mouse polling that I was hoping to implement as well, and I'm not sure that Cocoa supports polling keyboard state - although I will be happy to be corrected.
TIA.

Handle "new top level window" events in Xlib/Xt

So I'm in a situation where I need to know when a top level window gets created. I'm working at the Xlib/Xt level and on a Window Manager that doesn't support the EWMH specification. My idea is to hook into the root window's SubstructureNotify events. But things are not as simple as just that.
The problem is that not every CreateNotify event corresponds to the creation of a [b]top level[/b] window. So what I think I need to do is test the window I get from the event somehow to confirm that it is a top level window. I've got close, but some spurious windows still make it through my net. For example, in a GTK application if you have a dropdown box and you click it, a new window is created that I can't figure out how to catch and ignore. Such a window is troublesomely indistinguishable from a typical top level application window.
Here's what I have so far:
// I am omiting (tons of) cleanup code and where I set the display and toplevel variables.
Display* display;
Widget toplevel;
bool has_name(Window window)
{
XTextProperty data = XTextProperty ();
return (!XGetWMName (display, window, &data));
}
bool has_client_leader(Window window)
{
unsigned long nitems = 0;
unsigned char* data = 0;
Atom actual_type;
int actual_format;
unsigned long bytes;
// WM_CLIENT_LEADER is an interned Atom for the WM_CLIENT_LEADER property
int status = XGetWindowProperty (display, window, WM_CLIENT_LEADER, 0L, (~0L), False,
AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes, &data);
if (status != Success || acutal_type == None) return false;
Window* leader = reinterpret_cast<Window*> (data);
return (*leader != 0);
}
bool has_class(Window window)
{
XClassHint data = XClassHint ();
return (!GetClassHint (display, window, &data));
}
void handle_event(Widget widget, XtPointer, XEvent* event, Boolean*)
{
if (event->type != CreateNotify) return;
Window w = event->xcreatewindow.window;
// confirm window has a name
if (!has_name (w)) return;
// confirm window is a client window
Window client = XmuClientWindow (display, w);
if (!client || client != w) return;
// confirm window has a client leader that is not 0x0
if (!has_client_leader (client)) return;
// confirm window has a class
if (!has_class (client)) return;
// The window has passed all our checks!
// Go on to do stuff with the window ...
}
int main(int argc, char* argv[])
{
// ...
// Setting up the event handler for SubstructureNotify on root window
Window root_window = XDefaultRootWindow (display);
Widget dummy = XtCreateWidget ("dummy", coreWidgetClass, toplevel, 0, 0);
XtRegisterDrawable (display, root_window, dummy);
XSelectInput (display, root_window, SubstructureNotifyMask);
XtAddRawEventHandler (dummy, SubstructureNotifyMask, False, handle_event, 0);
// ...
}
A long shot, but does anyone have any ideas I could try? I can't think of much else I can really do here.
I assume you're familiar with the ICCCM and its long-winded discussion.
Have you checked the WM_TRANSIENT_FOR property?