I'm working on a Unity project in *Linux where I need to display the game/project on 2 screens using only 1 camera and 1 "display", for better performance.
What can i do to solve this problem? I will need set or modify something in the OS? Can i do it programmatically?
I'm using:
Debian with XFCE
Unity3d 2020.3.16f1
Rendering Pipeline: URP 2D
Edit:
For a exemple: I want to "combine" both monitors/display to act as one. So when i have two screen of 1920x1080 the game need to "extend" the window and work as if the screen was 1920x2160.
Current i tried to used the follow command in unity:
Screen.SetResolution(int width, int height, FullScreenMode
fullscreenMode);
with:
Width: 1920
Height: 2160
FullScreenMode: ExclusiveFullScreen, FullScreenWindow ,MaximizedWindow, Windowed.
In all the modes the game stayed in only one screen, liming it self to 1920x1080.
In Unity 2020.3.16f1, I couldn't find any direct solution for Linux (Debian).
The solution I found is to meddle with the X windows server, Xorg has been used as default since Debian 4.0.
The option I used:
Xorg has a module called Xrandr (Linux users probably already know that) that provides the xrandr commands in terminal and interacts with the configuration of the displays.
To check what displays are connected I use the command xrandr --listmonitors | grep '+' | awk {'print $4'}.
For me it return:
HDMI-0
HDMI-1
So to combine the display use the command xrandr --setmonitor CustomName auto HDMI-0,HDMI-1}, this create a virtual display.
As I want to set one screen above the other I use this xrandr --output HDMI-1 --above HDMI-0.
Now both display work as one.
I want Unity to call these commands, so that when the game opens it sets the configuration automatically, but because I am using the IL2CPP Scripting Backend I can't use Application.Process to call the commands. To solve that I create a lib in C++ 11, libSystemCommands.so, to execute the commands.
system_commands.cpp
#include "system_commands.h"
#include <vector>
#include <array>
#include <memory>
#include <cstring>
void system_command(const char * command){
std::system(command);
}
char* system_command_with_output(const char * command){
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE , decltype(&pclose)> pipe(popen(command,"r"), pclose);
if (!pipe) throw std::runtime_error("popopen() failed!");
while (fgets(buffer.data(),buffer.size(), pipe.get())!= nullptr)
result += buffer.data();
char * res = (char *) malloc(strlen(result.c_str())+1);
strcpy(res,result.c_str());
return res;
}
system_commands.h
#ifndef SYSTEMCOMMAND_LIBRARY_H
#define SYSTEMCOMMAND_LIBRARY_H
#if defined(_MSC_VER)
#ifdef NativePlugin_EXPORTS
#define NATIVEPLUGIN_API extern "C" __declspec(dllexport)
#else
#define NATIVEPLUGIN_API extern "C" __declspec(dllimport)
#endif
#else
#define NATIVEPLUGIN_API extern "C"
#endif
NATIVEPLUGIN_API void system_command(const char * command);
NATIVEPLUGIN_API char* system_command_with_output(const char * command);
#endif //SYSTEMCOMMAND_LIBRARY_H
In Unity I create the following script to control the changes:
DisplayManager.cs
using System;
using System.Runtime.InteropServices;
public static class DisplayManager
{
//List of displays/monitors connected
private static string[] _displays;
private static string[] _originalPositions;
//Detect the displays/monitors connected, using xrandr
public static void DetectDisplays()
{
//Get and set the display connected, to a list.
string result = system_command_with_output("xrandr --listmonitors | grep '+' | awk {'print $4'}");
_displays = result.Split(new string[] { Environment.NewLine },
StringSplitOptions.RemoveEmptyEntries);
// We want to run the rest only if there is two or more screens.
if (_displays.Length<=1) return;
// Get the original position of the two first display/monitors.
result = system_command_with_output(
"xrandr --listmonitors | grep '+' | awk '{print $3}' | awk -F'+' '{print $2,$3}'");
result = result.Replace(' ', 'x');
_originalPositions = result.Split(new string[] { Environment.NewLine },
StringSplitOptions.RemoveEmptyEntries);
}
//Combine two displays/monitors in one using xrandr
public static void CombineDisplays()
{
// We want to run this only if there is two or more screens.
if (_displays.Length<=1) return;
// xrandr command to create a virtual monitor combining others monitors.
system_command($"xrandr --setmonitor DisplayCombination auto {_displays[0]},{_displays[1]}");
// xrandr don´t apply the new virtual monitor automatically and don´t set a change if the input
// is equal of the current state. So the two command is to make sure to apple de combination and
// the display is one above another.
system_command($"xrandr --auto & xrandr --output {_displays[1]} --left-of {_displays[0]}");
// Set the desired position.
system_command($"xrandr --auto & xrandr --output {_displays[1]} --above {_displays[0]}");
}
//Reset the Display to before the Combination.
public static void ResetDisplays()
{
// Delete the virtual display created, if it exist
system_command("xrandr --delmonitor DisplayCombination");
// We want to run this only if there is two or more screens.
if (_displays.Length<=1) return;
// xrandr don´t apply the deletion of the virtual display automatically and don´t set a change
// if the input is equal of the current state. So the two command is to make sure to apple de
// deletion and reset the displays/monitors to the original position.
system_command($"xrandr --auto & xrandr --output {_displays[1]} --left-of {_displays[0]}");
//Set the Displays to their original position.
for(int i = 0; i <_displays.Length;i++)
system_command($"xrandr --output {_displays[i]} --pos {_originalPositions[i]}");
}
// function from the lib that intermediate the command execution with the system.
[DllImport("SystemCommands")]
private static extern void system_command(string command);
// function from the lib that intermediate the command execution with the system, with output.
[DllImport("SystemCommands")]
private static extern string system_command_with_output(string command);
}
And then in another script I only need to call this method and set the Screen.SetResolution to do the trick.
Example:
private int _newWidth;
private int _newHeight;
private void Awake()
{
_newWidth = Display.main.systemWidth;
_newHeight = Display.main.systemHeight;
if (Display.displays.Length > 1)
_newHeight += Display.displays[1].systemHeight;
DisplayManager.DetectDisplays();
DisplayManager.CombineDisplays();
}
private void Start()
{
Screen.SetResolution(_newWidth,_newHeight,FullScreenMode.FullScreenWindow);
}
private void OnApplicationQuit()
{
DisplayManager.ResetDisplays();
}
The other option (I didn’t check):
Is using the Xorg configuration folder, that is read when you login in account, together with the module Xinerama (I need to check if Xinerama is really needed).
Create a file at Xorg.conf.d, as ##-name.conf (“##” being a number identifies the priority of execution/reading, and “name” being something you can use to identify it more easily).
The folder can be at:
/etc/X11/Xorg.conf.d/
/user/shared/X11/Xorg.conf.d/
And you will need to setup a SERVERLAYOUT, and enable the option Xinerama.
Section "ServerLayout"
Identifier "name"
Screen "screen−id"
...
Option "Xinerama" "1"
...
EndSection
More information abou xorg at: https://www.x.org/releases/current/doc/man/man5/xorg.conf.5.xhtml#heading16
https://wiki.archlinux.org/title/multihead
Related
I am seeing a problem while restoring QMainWindow state having QCombobox in floating toolbar. After restoring floating toolbar, my QCombobox is not able to get focus until i click on toolbar handle and move it.
Following is gif showing problem, Using QT 5.13.
File floating_toolbar.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = floating_toolbar
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
CONFIG += c++11
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
File : main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
File : mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
void closeEvent(QCloseEvent *event);
void readSettings();
bool eventFilter(QObject* xObj, QEvent* xEvent);
~MainWindow();
public slots:
void mCheck();
};
#endif // MAINWINDOW_H
File : mainwindow.cpp
#include "mainwindow.h"
#include <QToolBar>
#include <QComboBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLayout>
#include <QSettings>
#include <QEvent>
#include <QDebug>
#include <QMouseEvent>
#include <QApplication>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QToolBar* lToolbar = new QToolBar(this);
QComboBox* lComobox = new QComboBox(this);
lComobox->setEditable(true);
lToolbar->setWindowTitle("MyToolBar");
lToolbar->setObjectName("NiceBaby");
lToolbar->addWidget(lComobox);
//lToolbar->addAction("check", lComobox, SLOT(clearEditText()));
addToolBar(lToolbar);
lToolbar->installEventFilter(this);
readSettings();
}
void MainWindow::mCheck()
{
}
void MainWindow::closeEvent(QCloseEvent *event)
{
QSettings settings("MyCompany", "MyApp");
settings.setValue("windowState", saveState());
QMainWindow::closeEvent(event);
}
void MainWindow::readSettings()
{
QSettings settings("MyCompany", "MyApp");
restoreState(settings.value("windowState").toByteArray());
}
MainWindow::~MainWindow()
{
}
bool MainWindow::eventFilter(QObject* xObj, QEvent* xEvent)
{
//qDebug()<<xEvent->type();
return QMainWindow::eventFilter(xObj, xEvent);
}
OK, a workaround is to reset the window flags on the toolbar when it is first shown and is floating. I tracked this down by seeing what happens once a toolbar is dropped after being dragged (but not plugged into main window). (It calls setWindowState() and all that does in this situation is hide the toolbar, call updateWindowFlags(), and show it again).
This could be handled from the QMainWindow::showEvent() or from the eventFilter installed onto the QToolBar. I think it's simpler from the former.
UPDATE: This problem actually happens whenever the toolbar is first shown even if not at app startup, e.g. from the toggle view menu by the user once app starts. I updated the code below to fix that issue as well. And see notes below about another issue with minimizing the main window.
I added this to the MainWindow class from the MCVE:
protected:
void showEvent(QShowEvent *e) override {
QMainWindow::showEvent(e);
#ifdef Q_OS_LINUX
if (lToolbar->isFloating()
// remove the next condition and the toolsbar will get hidden the 2nd time main window is minimized.
&& lToolbar->windowFlags().testFlag(Qt::X11BypassWindowManagerHint)
) {
const bool vis = !lToolbar->isHidden();
qDebug() << lToolbar->isFloating() << vis << lToolbar->windowFlags();
lToolbar->hide();
lToolbar->setWindowFlag(Qt::X11BypassWindowManagerHint, false);
if (vis)
lToolbar->show();
#endif
}
QToolBar* lToolbar; // Use this in MainWindow constructor to save the instance pointer.
I also noticed another issue with the initially-floating toolbar. When the main window is minimized, the toolbar doesn't get hidden but stays where it was on the screen. Regardless of what is in the toolbar (eg. no combo box, just QActions). This workaround could also sort-of address that issue (see code comment), but only the 2nd time the window is minimized. Needs a better workaround for the first minimize.
Can others confirm this? Potentially a larger issue than the editable combo and I'd be surprised if no one noticed before.
I guess this should be filed as a Qt bug either way.
UPDATE2: This version also fixes the minimize issue. I guess something happens after the QMainWindow::showEvent() that changes how the toolbar behaves. Which explains why the above workaround works only after the 1st minimize. So scheduling the toolbar "fix" for later works around that also.
class MainWindow : public QMainWindow
{
...
#ifdef Q_OS_LINUX
protected:
void showEvent(QShowEvent *e) override
{
QMainWindow::showEvent(e);
if (lToolbar->isFloating() && lToolbar->windowFlags().testFlag(Qt::X11BypassWindowManagerHint) ) {
// QMainWindow::show() after QMainWindow::restoreState() will break the minimizing again so we should delay calling adjustToolbar().
QMetaObject::invokeMethod(this, "adjustToolbar", Qt::QueuedConnection);
// If we're sure restoreState() is only called after show() then adjustToolbar() could be called here directly instead.
//adjustToolbar();
}
}
private slots:
void adjustToolbar() const
{
const bool vis = !lToolbar->isHidden();
qDebug() << lToolbar->isFloating() << vis << lToolbar->windowFlags();
lToolbar->hide();
lToolbar->setWindowFlag(Qt::X11BypassWindowManagerHint, false);
if (vis)
lToolbar->show();
}
#endif
private:
QToolBar* lToolbar;
};
ADDED: A QToolBar subclass which applies the workaround on its own, nothing special needed in the QMainWindow. The minimize fix still only works when the adjustToolbar() function is queued or if restoreState() is only called after show() (see code comments).
class ToolBar : public QToolBar
{
Q_OBJECT
public:
using QToolBar::QToolBar;
#ifdef Q_OS_LINUX
protected:
void showEvent(QShowEvent *e) override
{
QToolBar::showEvent(e);
if (isFloating() && windowFlags().testFlag(Qt::X11BypassWindowManagerHint) ) {
// QMainWindow::show() after QMainWindow::restoreState() will break the minimizing again so we should delay calling adjustToolbar().
QMetaObject::invokeMethod(this, "adjustToolbar", Qt::QueuedConnection);
// If we're sure restoreState() is only called after show() then adjustToolbar() could be called here directly instead.
//adjustToolbar();
}
}
private slots:
void adjustToolbar()
{
const bool vis = !isHidden();
hide();
setWindowFlag(Qt::X11BypassWindowManagerHint, false);
if (vis)
show();
}
#endif
};
UPDATE3: The minimizing issue also exists with floating QDockWidget if the QMainWindow state is restored before it is shown. In fact with "older" Qt versions the floating widget doesn't show up at all (doesn't with <= 5.9.5 but does with >= 5.12.4, don't have anything in between to try ATM). So the proper approach is to show() the main window first and then restoreState(). Unfortunately this doesn't seem to work for QToolBar.
UPDATE4: Filed as QTBUG-78293
It seems to work normally on macOS:
How would we go about changing the system time on a linux system programatically using QT widget application ?
You can use dbus to interface with timedated daemon https://www.freedesktop.org/wiki/Software/systemd/timedated/
to set time and date.
Qt provides a way to generate interface code from xml
http://doc.qt.io/qt-5/qdbusxml2cpp.html. You can get xml by introspection.
I don't like generated code formatting so I wrote interface code myself
h:
#ifndef TIMEDATE1SERVICE_H
#define TIMEDATE1SERVICE_H
#include <QObject>
#include <QString>
#include <QVariant>
#include <QtDBus>
class Timedate1Interface: public QDBusAbstractInterface
{
Q_OBJECT
Q_PROPERTY(bool CanNTP READ CanNTP)
Q_PROPERTY(bool LocalRTC READ LocalRTC)
Q_PROPERTY(bool NTP READ NTP)
Q_PROPERTY(bool NTPSynchronized READ NTPSynchronized)
Q_PROPERTY(qulonglong RTCTimeUSec READ RTCTimeUSec)
Q_PROPERTY(qulonglong TimeUSec READ TimeUSec)
Q_PROPERTY(QString Timezone READ Timezone)
public:
explicit Timedate1Interface(QObject *parent = nullptr);
bool CanNTP() const;
bool LocalRTC() const;
bool NTP() const;
bool NTPSynchronized() const;
qulonglong RTCTimeUSec() const;
qulonglong TimeUSec() const;
QString Timezone() const;
void SetLocalRTC(bool localRTC, bool fixSystem, bool userInteraction);
void SetNTP(bool useNTP, bool userInteraction);
void SetTime(qlonglong usecUTC, bool relative, bool userInteraction);
void SetTimezone(const QString &timezone, bool userInteraction);
};
#endif // TIMEDATE1SERVICE_H
cpp:
#include "timedate1service.h"
Timedate1Interface::Timedate1Interface(QObject *parent)
: QDBusAbstractInterface("org.freedesktop.timedate1", "/org/freedesktop/timedate1",
"org.freedesktop.timedate1", QDBusConnection::systemBus(), parent)
{
}
bool Timedate1Interface::CanNTP() const
{
return qvariant_cast<bool>(property("CanNTP"));
}
bool Timedate1Interface::LocalRTC() const
{
return qvariant_cast<bool>(property("LocalRTC"));
}
bool Timedate1Interface::NTP() const
{
return qvariant_cast<bool>(property("NTP"));
}
bool Timedate1Interface::NTPSynchronized() const
{
return qvariant_cast<bool>(property("NTPSynchronized"));
}
qulonglong Timedate1Interface::RTCTimeUSec() const
{
return qvariant_cast<qulonglong>(property("RTCTimeUSec"));
}
qulonglong Timedate1Interface::TimeUSec() const
{
return qvariant_cast<qulonglong>(property("TimeUSec"));
}
QString Timedate1Interface::Timezone() const
{
return qvariant_cast<QString>(property("Timezone"));
}
void Timedate1Interface::SetLocalRTC(bool localRTC, bool fixSystem, bool userInteraction)
{
call("SetLocalRTC", localRTC, fixSystem, userInteraction);
}
void Timedate1Interface::SetNTP(bool useNTP, bool userInteraction)
{
call("SetNTP", useNTP, userInteraction);
}
void Timedate1Interface::SetTime(qlonglong usecUTC, bool relative, bool userInteraction)
{
call("SetTime", usecUTC, relative , userInteraction);
}
void Timedate1Interface::SetTimezone(const QString &timezone, bool userInteraction)
{
call("SetTimezone", timezone, userInteraction);
}
You cannot do that in pure Qt. You need to use Linux (or POSIX) specific things.
And you probably should not do that, but better yet configure your entire system to use NTP (e.g. by running some NTP client...). Most Linux distributions have that already.
If you really want to set the system time (but you should not do that directly from a Qt application, since Qt applications should not run as root, but see this), read time(7) then adjtimex(2) & settimeofday(2)
You need to be root for that, so you should not do that from a Qt application. You might use setuid techniques to run some specific command or program as root. Setuid is tricky (see credentials(7), execve(2), setreuid(2)...), could open a giant security hole if misused (and making mistakes is easy), so read something about Linux programming, e.g. the old ALP.
So if you insist doing that (and it is probably wrong), write a tiny specific program in C for that and make it setuid and run that setuid-program from your Qt application (e.g. using QProcess).
I found a simple solution. As my system is very minimalist i dont want to use things like dbus. As a root or sudoer this can be execute (fairly self explainatory )-
QString string = dateTime.toString("\"yyyy-MM-dd hh:mm\"");
QString dateTimeString ("date -s ");
dateTimeString.append(string);
int systemDateTimeStatus= system(dateTimeString.toStdString().c_str());
if (systemDateTimeStatus == -1)
{
qDebug() << "Failed to change date time";
}
int systemHwClockStatus = system("/sbin/hwclock -w");
if (systemHwClockStatus == -1 )
{
qDebug() << "Failed to sync hardware clock";
}
I can create proc file.what I want to learn graphic card info using proc read function.How can I do this?
I do not mean to learn that info in terminal( by writing lspci vs).
do you know the path of which file stores the graphic card info in /proc directory?
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
// read proc function
int read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *data) {
/* file to be read? */
return 1;
}
// Module loading..
static int start(void){
create_proc_read_entry("myproc", 0, NULL, read_proc, NULL);
return 0;
}
static void fin(void) {
remove_proc_entry("myproc", NULL);
}
module_init(start);
module_exit(fin);
I am not completely sure if I understand what your question is actually about. But in case you're asking where you can read information about your graphics card(s), here are my 0.01€.
PCI device information is available under /sys/bus/pci/devices.
Use lspci to find out the device number(s) for your graphics card(s). For example: lspci |grep -i graphics|awk '{ print $1 }'.
Then see appropriate subdirectories in /sys/bus/pci/devices.
I know I can set thread name (the one visible in gdb and htop) in Linux using prctl(). But with another OSes this most likely won't work. Also, I could try using pthread_setname_np(), which is a bit more available across POSIX systems, but still lacks full compatibility.
So I'd like to have some more portable way, maybe something QThread provides which I've not found. Is there any such way?
There's nothing in the QThread API to manually manage the system name of the thread, however, since version 4.8.3, Qt will automatically set the name of your thread to the name of the thread object (QObject::objectName()).
This is handled in the implementations of QThread as described below.
You have something like this in qthread_unix.cpp:
#if (defined(Q_OS_LINUX) || defined(Q_OS_MAC) || defined(Q_OS_QNX))
static void setCurrentThreadName(pthread_t threadId, const char *name)
{
# if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
Q_UNUSED(threadId);
prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0);
# elif defined(Q_OS_MAC)
Q_UNUSED(threadId);
pthread_setname_np(name);
# elif defined(Q_OS_QNX)
pthread_setname_np(threadId, name);
# endif
}
#endif
/*
* [...]
*/
QString objectName = thr->objectName();
if (Q_LIKELY(objectName.isEmpty()))
setCurrentThreadName(thr->d_func()->thread_id, thr->metaObject()->className());
else
setCurrentThreadName(thr->d_func()->thread_id, objectName.toLocal8Bit());
And the equivalent in qthread_win.cpp:
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // must be 0x1000
LPCSTR szName; // pointer to name (in user addr space)
HANDLE dwThreadID; // thread ID (-1=caller thread)
DWORD dwFlags; // reserved for future use, must be zero
} THREADNAME_INFO;
void qt_set_thread_name(HANDLE threadId, LPCSTR threadName)
{
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = threadName;
info.dwThreadID = threadId;
info.dwFlags = 0;
__try
{
RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD), (const ULONG_PTR*)&info);
}
__except (EXCEPTION_CONTINUE_EXECUTION)
{
}
}
/*
* [...]
*/
QByteArray objectName = thr->objectName().toLocal8Bit();
qt_set_thread_name((HANDLE)-1, objectName.isEmpty() ? thr->metaObject()->className() : objectName.constData());
Note that on Windows, the above code won't be executed if QT_NO_DEBUG is set, thus it won't work in Release mode.
In Qt documentation you can find:
To choose the name that your thread will be given (as identified by
the command ps -L on Linux, for example), you can call setObjectName()
before starting the thread. If you don't call setObjectName(), the
name given to your thread will be the class name of the runtime type
of your thread object (for example, "RenderThread" in the case of the
Mandelbrot Example, as that is the name of the QThread subclass). Note
that this is currently not available with release builds on Windows.
I ran into troubles trying to create a gnome-panel applet with gtkmm. I dealt with most of them, but I'm now kind of blocked.
Quick summary : I tried libpanelappletmm, but every program (even the examples supplied in the source code) segfaults when I try to add the applet in my panel.
So I now use the C library (libpanel-applet). First I looked for a way to wrap the PanelApplet Gobject in a gtkmm C++-object, for example a Gtk::EventBox (PanelApplet inherits from GtkEventBox). I tried to cast it, but Glibmm kept throwing a warning ("Failed to wrap object 'PanelApplet'").
So I created a class "Info", inheriting from Gtk::HBox. In my main.cpp file I declare an instance of it, get the underlying GTK object (gobj method), and use the GTK+ functions to add it into the PanelApplet.
Here's my main.cpp.
#include <iostream>
#include <gtkmm.h>
#include <panel-applet.h>
#include "Info.hpp"
static void manage_timeboxes(BonoboUIComponent *uic, void *applet, const char* data) {
std::cout << "manage" << std::endl;
}
static gboolean getApplet(PanelApplet *applet, const gchar *iid, gpointer data) {
/*
if(iid != "OAFIID:TimeboxingApplet")
return false;
*/
Glib::init();
Gtk::Widget* content = new Info();
gtk_container_add(GTK_CONTAINER(applet), content->gobj());
static const char menu_xml[] =
"<popup name=\"button3\">\n"
" <menuitem name=\"Manage\" "
" verb=\"manage_timeboxes\" "
" _label=\"_Gérer l'emploi du temps\"\n"
" pixtype=\"stock\" "
" pixname=\"gtk-properties\"/>\n"
"</popup>\n";
static const BonoboUIVerb linked_verbs[] = {
BONOBO_UI_VERB ("manage_timeboxes", manage_timeboxes),
BONOBO_UI_VERB_END
};
panel_applet_setup_menu(applet, menu_xml, linked_verbs, data);
gtk_widget_show_all(GTK_WIDGET(applet));
return true;
}
PANEL_APPLET_BONOBO_FACTORY (
"OAFIID:TimeboxingApplet_Factory",
PANEL_TYPE_APPLET,
"Timeboxing",
"0.0",
getApplet,
NULL)
It works fine if I add labels or buttons in my Info object.
But then I tried to add an icon.
My first try was adding a Gtk::Image as a property of Info.
Info.hpp
#ifndef TIMEBOXING_INFO_H
#define TIMEBOXING_INFO_H
#include <gtkmm/box.h>
#include <gtkmm/image.h>
#include <gtkmm/label.h>
class Info : public Gtk::HBox {
public:
Info();
virtual ~Info(){};
protected:
Gtk::Image icon;
Gtk::Label info;
};
#endif
Info.cpp
#include "Info.hpp"
#include <gtkmm/image.h>
#include <gtkmm/label.h>
Info::Info() : icon("/home/bastien/programmation/timeboxing-applet/icons/clock-24.png"), info("<b>En cours</b>") {
info.set_use_markup();
pack_start(icon);
pack_start(info);
show_all_children();
}
When I try to add the applet, I get this error and the program aborts :
glibmm:ERROR:objectbase.cc:78:void Glib::ObjectBase::initialize(GObject*): assertion failed: (gobject_ == castitem)
I commented "Gtk::Image icon" from Info.hpp, and I modified my constructor like this :
Info::Info() : info("<b>En cours</b>") {
info.set_use_markup();
Gtk::Image icon("/home/bastien/programmation/timeboxing-applet/icons/clock-24.png");
pack_start(icon);
pack_start(info);
show_all_children();
}
I'm not getting the Glibmm error anymore, but the image isn't displayed. I tried with another file, with an icon from the stock, and even with a Gdk::Pixbuf.
Thank you in advance !
Well, strangely enough, it works if I create a pointer to Gtk::Image.
If anyone has an explanation, it would be great !
Edit : apparently, I had to call Gtk::Main::init_gtkmm_internals. My wrapping troubles went away. I can wrap PanelApplet too, but if I use the resulting Gtk::EventBox* it doesn't display anything.