Context
I am toggling the presence of the mouse cursor depending on whether or not a mouse is plugged into my Linux system. I have created a solution that works, although sub optimally. My current solution is the following:
Modified WM: I extended a program supplied with my WM, matchbox-remote.c (see original source here), to allow a remote command that changes the cursor in real time. My change is based on this SO answer to a similar question.
Rules for udev: I added two udev rules that call matchbox-remote when a mouse is plugged in or unplugged, with my toggle argument.
Systemd service: I added a oneshot systemd service enables or disables the cursor on boot (depending on whether a mouse is plugged in or not). This addresses an edge-case where X is not yet ready when the udev rules first run.
If you would like to see how I have accomplished each step, move to the bottom where I have numbered sections with the relevant files
Problem
My solution works, but is suboptimal for two reasons:
The mouse cursor is present onscreen for a few seconds after the desktop loads if no mouse is plugged in, because the systemd rule runs after multi-user.target.
The mouse cursor remains present onscreen sometimes after being disabled. It disappears as soon as I interact with a button or other UI element. However, this can be quite annoying.
Attempted fixes
I have tried to do the following to improve my solution:
Reduce latency on boot: I tried to adjust my service file to start earlier in the boot progress. I specifically targeted it to start after xserver-nodm.service. However, this still fails to find the DISPLAY despite me manually setting it as an environment variable.
Disappear mouse pointer on event: I attempted to restart the WM, hoping that this would more seamlessly make the cursor disappear when it was supposed to. Unfortunately, this is a regressive solution as it makes the screen go black for several seconds and is more disruptive.
Conclusion
I would like help in solving the following:
How can I change the cursor in a timely fashion at boot time (Do I need to modify X server or somehow queue events to send once it's reachable?)
How can I ensure a change to the cursor is reflected immediately (as opposed to sometimes waiting until I interact with a UI element)?
More info
Kernel: Linux 5.4.3
Xorg: Version 1.20.5
(1): matchbox-remote.c (in /usr/bin/)
Display *dpy;
...
#include <X11/cursorfont.h>
#include <X11/extensions/Xfixes.h>
...
void set_show_cursor (int show)
{
Window root;
Cursor cursor;
Pixmap bitmap;
XColor color;
static char data[8] = {0};
root = DefaultRootWindow(dpy);
if (!show) {
color.red = color.green = color.blue = 0;
bitmap = xCreateBitmapFromData(dpy, root, data, 8, 8);
cursor = XCreatePixmapCursor(dpy, bitmap, bitmap, &color, &color, 0, 0);
XDefineCursor(dpy, root, cursor);
XFreeCursor(dpy, cursor);
XFreePixmap(dpy, bitmap);
} else {
cursor = XCreateFontCursor(dpy, XC_left_ptr);
XDefineCursor(dpy, root, cursor);
XFreeCursor(dpy, cursor);
}
}
...
static void usage(char *progname) {
...
printf(" -show-cursor [1|0] Enable or disable the cursor\n");
...
}
...
int main (int argc, char* argv[])
{
...
for (i=1; argv[i]; i++) {
...
switch (arg[1])
{
....
case 's':
if (NULL != argv[i+1]) {
set_show_cursor(atoi(argv[i+1]));
}
break;
...
}
}
XSync(dpy, False);
XCloseDisplay(dpy);
}
(2): 98-cursor-toggle.rules (in /etc/udev/rules.d)
SUBSYSTEMS="usb", ACTION=="add", ENV{ID_INPUT_MOUSE}=="1", RUN+="/bin/sh -c 'DISPLAY=:0 /usr/bin/matchbox-remote -show-cursor 1'"
SUBSYSTEMS="usb", ACTION=="remove", ENV{ID_INPUT_MOUSE}=="1", RUN+="/bin/sh -c 'DISPLAY=:0 /usr/bin/matchbox-remote -show-cursor 0'"
(3) cursor-init.service (in /lib/systemd/system)
[Unit]
Description=X11 cursor initialisation
After=multi-user.target
Requires=multi-user.target
[Service]
Type=simple
ExecStart=/bin/sh -c 'DISPLAY=:0 /usr/bin/matchbox-remote -show-cursor $(ls -1 /dev/input/by-*/*-mouse 2>/dev/null | wc -l)'
RemainAfterExit=true
StandardOutput=journal
Restart=on-failure
RestartUSec=500000
[Install]
WantedBy=graphical.target
Related
I’m working in C on a project to capture data from a sensor and display it as part of a GUI application on the Raspberry Pi. I am using GTK 3.0, plus Cairo for graphing. I have built an application that works, but I want to make a modification to enable me to change the frequency of data capture.
Within my main code section I have a command like:-
gdk_threads_add_timeout (250, data_capture, widgets);
This all works, the data capture routine is triggered every 250mS, but I want to add functionality to the GUI to enable the user to change the speed. If I try to call this function from anywhere else other than main, it fails.
I have looked for other ways to do it, but I can’t find any examples or explanations of how I can do it.
Ideally what I would like is something like:-
void update_speed(button, widgets)
// Button to change speed has been pressed
read speed from GUI
update frequency
return
int main()
...
setup GUI
set default speed
start main GTK loop
Does anyone have any idea how I could achieve this?
Edit: Additional Code Snippet
(This is not the whole program, but an extract of main)
int main(int argc, char** argv) {
GtkBuilder *builder;
GtkWidget *window;
GError *err = NULL; // holds any error that occurs within GTK
// instantiate structure, allocating memory for it
struct app_widgets *widgets = g_slice_new(struct app_widgets);
// initialise GTK library and pass it in command line parameters
gtk_init(&argc, &argv);
// build the gui
builder = gtk_builder_new();
gtk_builder_add_from_file (builder, "../Visual/gui/main_window.glade", &err);
window = GTK_WIDGET(gtk_builder_get_object(builder, "main_application_window"));
// build the structure of widget pointers
widgets->w_spn_dataspeed = GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "spn_dataspeed"));
widgets->w_spn_refreshspeed = GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "spn_refreshspeed"));
widgets->w_adj_dataspeed = GTK_ADJUSTMENT(gtk_builder_get_object(builder, "adj_dataspeed"));
widgets->w_adj_refreshspeed = GTK_ADJUSTMENT(gtk_builder_get_object(builder, "adj_refreshspeed"));
// connect the widgets to the signal handler
gtk_builder_connect_signals(builder, widgets); // note: second parameter points to widgets
g_object_unref(builder);
// Set a timeout running to refresh the screen
gdk_threads_add_timeout(SCREEN_REFRESH_TIMER, (GSourceFunc)screen_timer_exe, (gpointer)widgets);
gdk_threads_add_timeout(DATA_REFRESH_TIMER, (GSourceFunc)data_timer_exe, (gpointer)widgets);
gtk_widget_show(window);
gtk_main();
// free up memory used by widget structure, probably not necessary as OS will
// reclaim memory from application after it exits
g_slice_free(struct app_widgets, widgets);
return (EXIT_SUCCESS);
There are a number of questions and answers about setting wallpapers programmatically on multi-monitor setups in Windows, but I'm asking specifically for Windows 10 (and maybe Windows 8) because it seems to work differently from all the explanations I found.
Raymond Chen has an article "How do I put a different wallpaper on each monitor?" (https://devblogs.microsoft.com/oldnewthing/?p=25003), also quoted in Monitors position on Windows wallpaper. The core concepts is that Windows places the top-left corner of the provided bitmap at the top-left corner of the primary monitor, and wraps around to fill any desktop space to the left and/or above that. I understand that, I wrote a little program using that knowledge, and it works beautifully in Windows 7.
How it works: I create a bitmap that conceptually covers the whole desktop space, as the user sees it. I draw the contents of each monitor to that bitmap in its appropriate position (the program is written in C++ using VCL, but the principle remains the same in other programming environments):
TRect GetMonitorRect_WallpaperCoords(int MonitorNum)
{
Forms::TMonitor *PrimaryMonitor = Screen->Monitors[0];
Forms::TMonitor *Monitor = Screen->Monitors[MonitorNum];
// Get the rectangle in desktop coordinates
TRect Rect(Monitor->Left, Monitor->Top, Monitor->Left + Monitor->Width, Monitor->Top + Monitor->Height);
// Convert to wallpaper coordinates
Rect.Left += PrimaryMonitor->Left - Screen->DesktopLeft;
Rect.Top += PrimaryMonitor->Top - Screen->DesktopTop;
Rect.Right += PrimaryMonitor->Left - Screen->DesktopLeft;
Rect.Bottom += PrimaryMonitor->Top - Screen->DesktopTop;
return Rect;
}
std::unique_ptr<Graphics::TBitmap> CreateWallpaperBitmap_WallpaperCoords()
{
std::unique_ptr<Graphics::TBitmap> Bmp(new Graphics::TBitmap);
Bmp->PixelFormat = pf24bit;
Bmp->Width = Screen->DesktopWidth;
Bmp->Height = Screen->DesktopHeight;
// Draw background (not that we really need it: it will never be visible)
Bmp->Canvas->Brush->Style = bsSolid;
Bmp->Canvas->Brush->Color = clBlack;
Bmp->Canvas->FillRect(TRect(0, 0, Bmp->Width, Bmp->Height));
for (int MonitorNum = 0; MonitorNum < Screen->MonitorCount; ++MonitorNum)
{
TDrawContext DC(Bmp->Canvas, GetMonitorRect_WallpaperCoords(MonitorNum));
DrawMonitor(DC);
}
return Bmp;
}
(The draw context uses a coordinate translation rect so that the code int DrawMonitor function can draw in a rectangle like (0, 0, 1920, 1080) without having to wonder where in the full bitmap it is drawing, and with a clip rect so that DrawMonitor can not accidentally draw outside of the monitor it's drawing on).
Then I convert that bitmap to an image that will properly wrap around when placed at the top-left corner of the primary monitor (as Raymond Chen describes in his article):
std::unique_ptr<Graphics::TBitmap> ConvertWallpaperToDesktopCoords(std::unique_ptr<Graphics::TBitmap> &Bmp_WallpaperCoords)
{
std::unique_ptr<Graphics::TBitmap> Bmp_DesktopCoords(new Graphics::TBitmap);
Bmp_DesktopCoords->PixelFormat = Bmp_WallpaperCoords->PixelFormat;
Bmp_DesktopCoords->Width = Bmp_WallpaperCoords->Width;
Bmp_DesktopCoords->Height = Bmp_WallpaperCoords->Height;
// Draw Bmp_WallpaperCoords to Bmp_DesktopCoords at four different places to account for all
// possible ways Windows wraps the wallpaper around the left and bottom edges of the desktop
// space
Bmp_DesktopCoords->Canvas->Draw(Screen->DesktopLeft, Screen->DesktopTop, Bmp_WallpaperCoords.get());
Bmp_DesktopCoords->Canvas->Draw(Screen->DesktopLeft + Screen->DesktopWidth, Screen->DesktopTop, Bmp_WallpaperCoords.get());
Bmp_DesktopCoords->Canvas->Draw(Screen->DesktopLeft, Screen->DesktopTop + Screen->DesktopHeight, Bmp_WallpaperCoords.get());
Bmp_DesktopCoords->Canvas->Draw(Screen->DesktopLeft + Screen->DesktopWidth, Screen->DesktopTop + Screen->DesktopHeight, Bmp_WallpaperCoords.get());
return Bmp_DesktopCoords;
}
Then I install that bitmap as a wallpaper by writing the appropriate values in the registry and calling SystemParametersInfo with SPI_SETDESKWALLPAPER:
void InstallWallpaper(const String &Fn)
{
// Install wallpaper:
// There are 3 name/data pairs that have an effect on the desktop wallpaper, all under HKCU\Control Panel\Desktop:
// - Wallpaper (REG_SZ): file path and name of wallpaper
// - WallpaperStyle (REG_SZ):
// . 0: Centered
// . 1: Tiled
// . 2: Stretched
// - TileWallpaper (REG_SZ):
// . 0: Don't tile
// . 1: Tile
// We don't use the Wallpaper value itself; instead we use SystemParametersInfo to set the wallpaper.
// The file name needs to be absolute!
assert(Ioutils::TPath::IsPathRooted(Fn));
std::unique_ptr<TRegistry> Reg(new TRegistry);
Reg->RootKey = HKEY_CURRENT_USER;
if (Reg->OpenKey(L"Control Panel\\Desktop", false))
{
Reg->WriteString(L"WallpaperStyle", L"1");
Reg->WriteString(L"TileWallpaper", L"1");
Reg->CloseKey();
}
SystemParametersInfoW(SPI_SETDESKWALLPAPER, 1, Fn.c_str(), SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
But when I test it in Windows 10, it doesn't work properly anymore: Windows 10 puts the wallpaper completely in the wrong place. Seeing as other people have asked questions about multi-monitor wallpapers in the past, I'm hoping there are people with experience of it on Windows 10.
As far as I can see, Windows 10 places the top-left corner of the provided bitmap at the top-left corner of the desktop space (by which I mean the bounding rectangle of all monitors), instead of the top-left corner of the primary monitor. In code, that means: I leave out the ConvertWallpaperToDesktopCoords step, and then it works fine as far as I can see.
But I can't find any documentation on this, so I don't know if this is officially explanation of how Windows 10 does it. Use with care. Also I don't know when this different behavior started: in Windows 10, or maybe earlier in Windows 8.
The documentation for Fl_Tree in FLTK 1.3.4 says:
The callback() is invoked depending on the value of when()
FL_WHEN_RELEASE -- callback invoked when left mouse button is released on an item
FL_WHEN_CHANGED -- callback invoked when left mouse changes selection state
but I can't get the callback called if the mouse is released and I can't see a difference between both. Any ideas?
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Tree.H>
static void cb_(Fl_Tree*, void*)
{
printf ("callback\n");
}
int main()
{
Fl_Double_Window* w = new Fl_Double_Window(325, 325);
Fl_Tree* o = new Fl_Tree(25, 25, 255, 245);
o->callback((Fl_Callback*)cb_);
o->when(FL_WHEN_RELEASE);
o->add("foo/bar");
o->add("foo/baz");
o->end();
w->show();
return Fl::run();
}
this snippets outputs "callback" on every change, even if FL_WHEN_RELEASE is set.
If you have downloaded, the distribution, have a look at test/input.cxx and test/tree.cxx. Both have tests for the different when selections.
WHEN_CHANGED only makes sense on edit boxes, browsers and tables - you can verify the data as it is typed in. This does not happen with WHEN_RELEASE. For all other widgets, there is virtually no difference.
Edit
In order for release to fire every time, there are one of three options
Modify the source FL_Tree.cxx. Look for FL_Tree::select. Change alreadySelected to false.
If you look at the source, in the same routine, further down, it says
#if FLTK_ABI_VERSION >= 10301
If the library is built with FLTK_ABI_VERSION set to 10301, it will call the reselect but there is also a whole load of other stuff it will do when this #define is set since it affects all widgets
Comment out the #if FLTK_ABI_VERISON and corresponding #endif in FL_Tree::select.
I am not sure if there may be a memory leak here - its about quitting a simple popup window in GTK.
if (alertWindow == NULL) {
alertWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
alertLabel = gtk_label_new (" wrong input! connection should be either s,S,p or P.\n All other data as floating numbers" );
gtk_container_add(GTK_CONTAINER(alertWindow), alertLabel);
g_signal_connect (alertWindow, "destroy", G_CALLBACK (destroyAlert), NULL); //avslutar applikation
}
and the callback
static void destroyAlert(GtkWidget *widget, gpointer data) {
alertWindow = NULL;
}
I set the alertWindow to NULL so it can be created once again next time the user enters wrong input.
That should not leak memory. The window assumes a reference to the label, so the label will get destroyed and freed with the window. The window will get destroyed and freed when you click on the window title bar's close button (which is the only way to close that window according to the code that you have.)
However, I'd suggest doing it a different way:
GtkWidget *alertWindow = gtk_message_dialog_new(parentWindow,
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR,
GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE,
"Wrong input! Connection should be either s, S, p or P.\n"
"All other data as floating point numbers.");
gtk_dialog_run(GTK_DIALOG(alertWindow));
gtk_widget_destroy(alertWindow);
This has a few advantages; first, you get a more full-featured dialog with a friendly button for closing it. Second, you don't have to use a global variable that you then have to worry about whether it is NULL or not. Third, the dialog is modal (the rest of the application is disabled while the dialog is open), which is appropriate for an error message like this.
Even better would be to detect the wrong input as the user types, and use something like a GtkPopover to inform them of what kind of input belongs in each field.
I have a Dual-Display Graphics card, on my system (RHEL 6.3).
I have developed one simple application using qt creator (qt-4.8), which throws two different UIs.
When I execute this then both UIs starts in only one display.
What I need is my one UI should run on primary screen and one on secondary screen (i.e. 0.0 and 0.1).
How should I do this using qt-creator?
xclock -display :0.0
xclock -display :0.1
works fine.
You can use a QDesktopWidget to get screen information. It Allows you to query the amount of screens and the dimension of each one with
int QDesktopWidget::screenCount () const;
const QRect QDesktopWidget::availableGeometry ( int screen = -1 ) const;
From there, you can move your widget to any given screen. For instance, the following code move the widget to a given screen or to the default one if the specified screen is not available:
QDesktopWidget* w = QApplication::desktop();
//some value
int mydesiredscreen = 1;
//fallback to default screen if none
if(mydesiredscreen >= w->screenCount()) mydesiredscreen = -1;
QRect rect1 = w->availableGeometry(mydesiredscreen);
mywindow->move(rect1.topLeft());
Tejas,
To display your Second UI on Secondary Monitor you can use setParent property for your Second UI as :
int screenNumber = 1; /* Desired screen no */
QWidget secondaryUI_widget; /* Secondary UI Object which is to be displayed on secondary monitor */
QDesktopWidget myDesktopWidget; /* Create an object of QDesktopWidget */
secondUI_myDesktopWidget.setParent(myDesktopWidget(screenNumber));
The above line will set the desired screen on which you would like to display your page as parent for your UI object.
Now you can call show() function for your second UI anywhere in your program , the second UI will be displayed on desired screen number as being by screenNumber value