Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@ object_script.*.Debug
*.pro.user.*
*.qbs.user
*.qbs.user.*
.objs/
.mocs/
*.moc
moc_*.cpp
moc_*.h
.qrcs/
qrc_*.cpp
.uis/
ui_*.h
*.qmlc
*.jsc
Expand Down
5 changes: 5 additions & 0 deletions inc/liquidappwindow.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <QWebEngineView>
#include <QWebEngineFullScreenRequest>

#include "singleinstance.hpp"

#include "liquidappwebpage.hpp"

class LiquidAppWebPage;
Expand All @@ -22,6 +24,8 @@ class LiquidAppWindow : public QWebEngineView
explicit LiquidAppWindow(const QString* name);
~LiquidAppWindow(void);

bool isAlreadyRunning(void);
void run(void);
void setForgiveNextPageLoadError(const bool ok);

QSettings* liquidAppConfig;
Expand Down Expand Up @@ -60,6 +64,7 @@ public slots:

QString liquidAppWindowTitle;
QIcon iconToSave;
SingleInstance *singleInstance;

LiquidAppWebPage* liquidAppWebPage = Q_NULLPTR;
QWebEngineProfile* liquidAppWebProfile = Q_NULLPTR;
Expand Down
9 changes: 7 additions & 2 deletions inc/mainwindow.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@
#include <QScrollArea>
#include <QTableWidget>

#include "singleinstance.hpp"

class MainWindow : public QScrollArea
{
public:
MainWindow();
~MainWindow();

bool isAlreadyRunning(void);
void run(void);

protected:
void closeEvent(QCloseEvent *event) override;

Expand All @@ -24,7 +29,7 @@ class MainWindow : public QScrollArea

QTableWidget* appListTable;
QPushButton* createNewLiquidAppButton;
QSettings* settings;

QAction* quitAction;
QSettings* settings;
SingleInstance *singleInstance;
};
37 changes: 20 additions & 17 deletions liquid.pro
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
include($${PWD}/modules/modules.pri)

PROG_NAME = liquid

VERSION_MAJOR = 0
VERSION_MINOR = 7
VERSION_PATCH = 11
Expand All @@ -9,10 +13,9 @@ CONFIG += c++11
TEMPLATE = app

DESTDIR = build
PROG_NAME = liquid

SRC_DIR = src
INC_DIR = inc
SRC_DIR = src
FORMS_DIR = ui

OBJECTS_DIR = .objs
Expand All @@ -22,21 +25,21 @@ RCC_DIR = .qrcs

INCLUDEPATH += $${INC_DIR}

SOURCES += src/liquid.cpp \
src/liquidappcookiejar.cpp \
src/liquidappconfigwindow.cpp \
src/liquidappwebpage.cpp \
src/liquidappwindow.cpp \
src/main.cpp \
src/mainwindow.cpp \

HEADERS += inc/lqd.h \
inc/liquid.hpp \
inc/liquidappcookiejar.hpp \
inc/liquidappconfigwindow.hpp \
inc/liquidappwebpage.hpp \
inc/liquidappwindow.hpp \
inc/mainwindow.hpp \
HEADERS += $${INC_DIR}/lqd.h \
$${INC_DIR}/liquid.hpp \
$${INC_DIR}/liquidappcookiejar.hpp \
$${INC_DIR}/liquidappconfigwindow.hpp \
$${INC_DIR}/liquidappwebpage.hpp \
$${INC_DIR}/liquidappwindow.hpp \
$${INC_DIR}/mainwindow.hpp \

SOURCES += $${SRC_DIR}/liquid.cpp \
$${SRC_DIR}/liquidappcookiejar.cpp \
$${SRC_DIR}/liquidappconfigwindow.cpp \
$${SRC_DIR}/liquidappwebpage.cpp \
$${SRC_DIR}/liquidappwindow.cpp \
$${SRC_DIR}/main.cpp \
$${SRC_DIR}/mainwindow.cpp \

RESOURCES = res/resources.qrc

Expand Down
1 change: 1 addition & 0 deletions modules/modules.pri
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include($${PWD}/singleinstance/singleinstance.pri)
27 changes: 27 additions & 0 deletions modules/singleinstance/inc/singleinstance.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <QSharedMemory>
#include <QSystemSemaphore>
#include <QWidget>

class SingleInstance : public QObject
{
Q_OBJECT

public:
explicit SingleInstance(QWidget *parent = Q_NULLPTR, QString *instanceId = new QString());
~SingleInstance();

bool isAlreadyRunning(const bool raiseExisting = false);
static void raiseWindow(QWidget *window);

private:
struct SharedData
{
bool needToRaiseExistingWindow = false;
};

QSharedMemory *shMem;
QString shmemName;
QString smphorName;
};
16 changes: 16 additions & 0 deletions modules/singleinstance/singleinstance.pri
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
!win:!mac {
LIBS += -lX11 -lXfixes -lXinerama -lXext -lXcomposite
}

QT += core
CONFIG += c++11
TEMPLATE = lib

INC_DIR = inc
SRC_DIR = src

INCLUDEPATH += $${PWD}/$${INC_DIR}

HEADERS += $${PWD}/inc/singleinstance.hpp

SOURCES += $${PWD}/src/singleinstance.cpp
118 changes: 118 additions & 0 deletions modules/singleinstance/src/singleinstance.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#include <QTimer>

#include "singleinstance.hpp"

#if defined(Q_OS_WIN)
#include <windows.h>
#elif !defined(Q_OS_MACOS)
#include <X11/Xlib.h>
#endif

SingleInstance::SingleInstance(QWidget *parent, QString *instanceId) : QObject(parent)
{
shmemName = instanceId + QString("_shrdmmr");
smphorName = instanceId + QString("_smphr");
}

SingleInstance::~SingleInstance()
{
}

bool SingleInstance::isAlreadyRunning(const bool raiseExisting)
{
QSystemSemaphore sem(smphorName, 1);

// Fix danging memory on Linux
{
QSharedMemory fix(shmemName);
fix.attach();
}

shMem = new QSharedMemory(shmemName);
if (shMem->create(sizeof(SharedData))) { // This is the first instance
shMem->attach();

sem.acquire();
memset(shMem->data(), 0, shMem->size());
sem.release();

QTimer* t = new QTimer(this);
connect(t, &QTimer::timeout, this, [this]() {
QSystemSemaphore sem(smphorName, 1);
sem.acquire();
SharedData* data = reinterpret_cast<SharedData*>(shMem->data());
if (data->needToRaiseExistingWindow) {
SingleInstance::raiseWindow((QWidget*)parent());
data->needToRaiseExistingWindow = false;
}
sem.release();
});
t->start(0);
} else { // Another instance is already running
shMem->attach();

sem.acquire();
SharedData* data = reinterpret_cast<SharedData*>(shMem->data());
data->needToRaiseExistingWindow = raiseExisting;

return true;
}

return false;
}

void SingleInstance::raiseWindow(QWidget *window)
{
#if defined(Q_OS_WIN) // Windows
Window winId = window->effectiveWinId();

SetWindowPos(winId, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
SetWindowPos(winId, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
#elif defined(Q_OS_MACOS) // macOS
window->show();
window->raise();
window->activateWindow();
#else // GNU/Linux, FreeBSD, etc
Window winId = window->effectiveWinId();

if (winId > 0) {
Display* disp = XOpenDisplay(nullptr);

if (disp) {
XWindowAttributes attributes;

if (XGetWindowAttributes(disp, winId, &attributes)) {
XSetInputFocus(disp, winId, RevertToPointerRoot, CurrentTime);
XRaiseWindow(disp, winId);

// Show window if minimized, instead of just highlighting its icon
{
const Atom atom = XInternAtom(disp, "_NET_ACTIVE_WINDOW", True);

if (atom != None) {
XEvent xev;

xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.message_type = atom;
xev.xclient.display = disp;
xev.xclient.window = winId;
xev.xclient.format = 32;
xev.xclient.data.l[0] = 1;
xev.xclient.data.l[1] = 0;
xev.xclient.data.l[2] = None;
xev.xclient.data.l[3] = 0;
xev.xclient.data.l[4] = 0;

XSendEvent(disp, DefaultRootWindow(disp), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
}
}
}

XFlush(disp);
XCloseDisplay(disp);
}
}
#endif
}
Loading