├── .gitignore
├── .gitmodules
├── .qmake.conf
├── LICENSE
├── README.md
├── TODO
├── compositor
├── compositor.pro
├── fonts
│ ├── FontAwesome.otf
│ └── manzanit.pfb
├── grefsen.qrc
├── images
│ ├── background.jpg
│ ├── gee.svg
│ ├── grefsen-logo-on-silhouette.png
│ ├── grefsen-logo.png
│ └── grefsenkollen-silhouette.svg
├── main.cpp
├── processlauncher.cpp
├── processlauncher.h
├── qml
│ ├── Chrome.qml
│ ├── Keyboard.qml
│ ├── Output.qml
│ └── main.qml
├── stackableitem.cpp
└── stackableitem.h
├── example-config
├── example-config.pro
├── grefsen.conf
└── screen.qml
├── grefsen.pro
├── imports
├── Grefsen
│ ├── ConnmanPopover.qml
│ ├── Grefsen.pro
│ ├── LauncherIcon.qml
│ ├── LauncherMenu.qml
│ ├── LauncherMenuIcon.qml
│ ├── LeftSlidePanel.qml
│ ├── PanelClock.qml
│ ├── Popover.qml
│ ├── PopoverPanelItem.qml
│ ├── PopoverTrayIcon.qml
│ ├── QuitButton.qml
│ ├── RightSlidePanel.qml
│ ├── TuioTouchPanel.qml
│ ├── iconprovider.cpp
│ ├── iconprovider.h
│ ├── launchermodel.cpp
│ ├── launchermodel.h
│ ├── plugin.cpp
│ └── qmldir
└── imports.pro
├── screenshot.jpg
└── thirdparty
├── freefont
├── README
├── manzanit.license
└── manzanit.pfb
└── thirdparty.pro
/.gitignore:
--------------------------------------------------------------------------------
1 | # C++ objects and libs
2 |
3 | *.slo
4 | *.lo
5 | *.o
6 | *.a
7 | *.la
8 | *.lai
9 | *.so
10 | *.dll
11 | *.dylib
12 |
13 | # Qt-es
14 |
15 | /.qmake.cache
16 | /.qmake.stash
17 | *.pro.user
18 | *.pro.user.*
19 | *.qbs.user
20 | *.qbs.user.*
21 | *.moc
22 | moc_*.cpp
23 | qrc_*.cpp
24 | ui_*.h
25 | Makefile*
26 | *-build-*
27 |
28 | # QtCreator
29 |
30 | *.autosave
31 |
32 | #QtCtreator Qml
33 | *.qmlproject.user
34 | *.qmlproject.user.*
35 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "thirdparty/Font-Awesome"]
2 | path = thirdparty/Font-Awesome
3 | url = https://github.com/FortAwesome/Font-Awesome.git
4 | [submodule "thirdparty/qt-tuio-sender"]
5 | path = thirdparty/qt-tuio-sender
6 | url = https://github.com/ec1oud/qt-tuio-sender.git
7 |
--------------------------------------------------------------------------------
/.qmake.conf:
--------------------------------------------------------------------------------
1 | load(qt_build_config)
2 |
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
167 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Grefsen is a Qt/Wayland compositor providing a minimal desktop environment.
2 |
3 | 
4 |
5 | # Requirements
6 |
7 | * Qt 6 (if you need 5.x, use the 5.x branch)
8 | - qtbase, qtdeclarative, qtwayland
9 | - QtQuick.Controls
10 | * libQtXdg
11 | - build from [github](https://github.com/lxqt/libqtxdg/tree/wip_qt6) with Qt 6 and cmake
12 | * for the Connman network manager popover (optional): [libconnman-qt](https://git.merproject.org/mer-core/libconnman-qt)
13 | or from [github](https://github.com/sailfishos/libconnman-qt)
14 | * FontAwesome, but it's a submodule here
15 | * recommended: [freefonts](http://ibiblio.org/pub/linux/X11/fonts/freefonts-0.10.tar.gz) but hopefully your distro has that as a package
16 | - the clock uses Manzanita, which is also installed as a resource in the executable, just in case
17 | - WindsorDemi is another nice font, but it will fall back to others if that is not installed
18 |
19 | # Building
20 |
21 | ```
22 | git submodule update --init --recursive
23 | qmake
24 | make
25 | ```
26 |
27 | # Installation
28 |
29 | ```
30 | mkdir ~/.config/grefsen
31 | cp example-config/*.qml ~/.config/grefsen
32 | ```
33 |
34 | Then modify to taste. If you want the wallpaper shown in the screenshot, download
35 | the full-resolution version from
36 | [wikipedia](https://commons.wikimedia.org/wiki/File:Oslo_mot_Grefsentoppen_fra_Ekeberg.jpg)
37 | to your ~/.config/grefsen directory.
38 |
39 | # Running
40 |
41 | It can run as a window inside an X11 session.
42 |
43 | If you want to run it on the Linux console without an X11 session
44 | (which is much more efficient), you can use a startup script like this:
45 |
46 | ```
47 | #!/bin/sh
48 | export QT_QPA_PLATFORM=eglfs
49 | export QT_AUTO_SCREEN_SCALE_FACTOR=0 # don't embiggen stuff on "high-res" displays
50 | #export QT_QPA_EGLFS_PHYSICAL_WIDTH=480 # in case it's not detected
51 | #export QT_QPA_EGLFS_PHYSICAL_HEIGHT=270 # or you wish to override
52 | # try to restart if it crashes; write a log file
53 | ~/src/grefsen/grefsen -r -l /tmp/grefsen.log
54 | ```
55 |
56 | If you are on the console and have the problem that the keyboard, mouse etc.
57 | don't work (which should be fixed in Qt 5.6 and above, theoretically) you can
58 | try various input plugins (after rebooting via ssh, or the power button ;-) by adding
59 | ```-plugin EvdevTouch -plugin EvdevMouse -plugin EvdevTablet -plugin EvdevKeyboard```
60 | or
61 | ```-plugin libinput```
62 |
63 | The set of applications you can run inside is mostly limited to those
64 | that are built with Qt 5, so far. That includes a lot of KDE applications.
65 | It is intended to eventually be able to run weston and GTK3 apps too;
66 | that's mainly a matter of qtwayland having the XDG shell support finished.
67 | Grefsen does not include an embedded X server yet, but it might be possible.
68 |
69 | The application menu uses libqtxdg to find .desktop files and .menu files
70 | in a way which is compliant with the
71 | [XDG menu spec](https://specifications.freedesktop.org/menu-spec/menu-spec-1.0.html),
72 | which means that you have some control over the contents of the menu, if you
73 | don't mind editing XML files. But grefsen does not try to prevent you from
74 | running incompatible applications: they will simply fail to launch. So you
75 | might want a custom menu just for grefsen, whereas by default you will probably
76 | see all the same applications as you do in your usual desktop environment.
77 |
78 | [YAT](https://github.com/jorgen/yat) is another terminal alternative.
79 | It's planned to have better touchscreen support soon (flicking with your
80 | finger, text selection when you drag a mouse), whereas konsole still doesn't.
81 |
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 | eglfs: touch doesn't work
2 | raise/lower
3 | way to get in/out of fullscreen mode
4 | window decoration theme rather than server-side decorations
5 | because access to the QWindow is easier than dealing with limitations of Wayland protocol for window management
6 | alt-tab
7 | virtual desktops
8 | window flinging onto other display
9 | better hover handling
10 | other 3-finger window gestures?
11 | ctrl-alt-f key to get back to other consoles (chvt)
12 | map caps lock to control (QTBUG-53183)
13 | touch window drag/resize
14 | long-press menu
15 | dbus menubar & tray
16 | xwayland: root window transparent, or under the grefsen desktop background? or rootless?
17 | probably hard: decorate X windows (like Weston does)
18 | device hotplugging?
19 | show single-ended-arrow cursor over the resize edge if the window can only be enlarged, not shrunken (idea from MS Teams on macOS)
20 | use new calendar control (submodule?) https://code.qt.io/cgit/qt-extensions/qtquickcalendar.git
21 |
22 | to fix
23 | ------
24 | redundant cursor problem:
25 | - QT_QPA_EGLFS_HIDECURSOR env var works, but
26 | - WaylandCursorItem shows only the entered window's cursor, and doesn't change on leave
27 | - cursors set on items within the compositor are only shown via the EGLFS cursor, not WaylandCursorItem
28 | connman configuration - can't always turn technologies on and off
29 | the crash when killing applications via the titlebar close button
30 | click outside popups to close
31 | can't run grefsen inside grefsen (or is it impossible?)
32 |
33 | done
34 | ----
35 | basic sidebar with a basic launcher for konsole
36 | some custom window decorations
37 | launcher menu based on desktop files
38 | launchers for more apps: qtcreator and a browser at least
39 | -c for config
40 | clock and calendar
41 | xdg_shell protocol (weston and gtk apps don't run without it)
42 | 3-finger pinch to resize and move windows
43 | alt-drag, meta-drag
44 |
45 |
--------------------------------------------------------------------------------
/compositor/compositor.pro:
--------------------------------------------------------------------------------
1 | QT += gui qml quick
2 | CONFIG += link_pkgconfig
3 | QMAKE_CXXFLAGS += -std=c++17
4 | TARGET = ../grefsen
5 |
6 | SOURCES += *.cpp
7 |
8 | HEADERS += *.h
9 |
10 | INCLUDEPATH += /usr/include/glib-2.0 /usr/lib/glib-2.0/include
11 |
12 | OTHER_FILES = \
13 | qml/main.qml \
14 | qml/Output.qml \
15 | qml/Chrome.qml
16 |
17 | RESOURCES += grefsen.qrc
18 |
19 | OBJECTS_DIR = .obj
20 | MOC_DIR = .moc
21 | RCC_DIR = .rcc
22 |
23 | PKGCONFIG += glib-2.0
24 |
25 | sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS grefsen.pro
26 |
--------------------------------------------------------------------------------
/compositor/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
1 | ../../thirdparty/Font-Awesome/otfs/Font Awesome 5 Free-Regular-400.otf
--------------------------------------------------------------------------------
/compositor/fonts/manzanit.pfb:
--------------------------------------------------------------------------------
1 | ../../thirdparty/freefont/manzanit.pfb
--------------------------------------------------------------------------------
/compositor/grefsen.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | images/background.jpg
4 | qml/main.qml
5 | qml/Keyboard.qml
6 | qml/Output.qml
7 | qml/Chrome.qml
8 | fonts/FontAwesome.otf
9 | images/grefsen-logo-on-silhouette.png
10 | fonts/manzanit.pfb
11 |
12 |
13 |
--------------------------------------------------------------------------------
/compositor/images/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ec1oud/grefsen/05a6203a2210b57c366f36297b82c548e5653ddb/compositor/images/background.jpg
--------------------------------------------------------------------------------
/compositor/images/gee.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
66 |
--------------------------------------------------------------------------------
/compositor/images/grefsen-logo-on-silhouette.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ec1oud/grefsen/05a6203a2210b57c366f36297b82c548e5653ddb/compositor/images/grefsen-logo-on-silhouette.png
--------------------------------------------------------------------------------
/compositor/images/grefsen-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ec1oud/grefsen/05a6203a2210b57c366f36297b82c548e5653ddb/compositor/images/grefsen-logo.png
--------------------------------------------------------------------------------
/compositor/images/grefsenkollen-silhouette.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
78 |
--------------------------------------------------------------------------------
/compositor/main.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015 Shawn Rutledge
4 | **
5 | ** This file is free software; you can redistribute it and/or
6 | ** modify it under the terms of the GNU Lesser General Public
7 | ** License version 3 as published by the Free Software Foundation
8 | ** and appearing in the file LICENSE included in the packaging
9 | ** of this file.
10 | **
11 | ** This code is distributed in the hope that it will be useful,
12 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | ** GNU Lesser General Public License for more details.
15 | **
16 | ****************************************************************************/
17 |
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 | #include
30 | #include
31 | #include
32 | #include
33 |
34 | #include "processlauncher.h"
35 | #include "stackableitem.h"
36 |
37 | #include
38 | #include
39 | #include
40 | #include
41 | #include
42 | #include
43 | #include
44 |
45 | static QLatin1String glassPaneName("glassPane");
46 | static QByteArray grefsonExecutablePath;
47 | static qint64 grefsonPID;
48 | static void *signalHandlerStack;
49 | static QString logFilePath;
50 | static QElapsedTimer sinceStartup;
51 |
52 | QString grefsenConfigDirPath(QDir::homePath() + "/.config/grefsen/");
53 |
54 | extern "C" void signalHandler(int signal)
55 | {
56 | #ifdef Q_WS_X11
57 | // Kill window since it's frozen anyway.
58 | if (QX11Info::display())
59 | close(ConnectionNumber(QX11Info::display()));
60 | #endif
61 | pid_t pid = fork();
62 | switch (pid) {
63 | case -1: // error
64 | break;
65 | case 0: // child
66 | kill(grefsonPID, 9);
67 | fprintf(stderr, "crashed (PID %lld SIG %d): respawn %s\n", grefsonPID, signal, grefsonExecutablePath.constData());
68 | execl(grefsonExecutablePath.constData(), grefsonExecutablePath.constData(), (char *) 0);
69 | _exit(EXIT_FAILURE);
70 | default: // parent
71 | prctl(PR_SET_PTRACER, pid, 0, 0, 0);
72 | waitpid(pid, 0, 0);
73 | _exit(EXIT_FAILURE);
74 | break;
75 | }
76 | }
77 |
78 | static void setupSignalHandler()
79 | {
80 | // Setup an alternative stack for the signal handler. This way we are able to handle SIGSEGV
81 | // even if the normal process stack is exhausted.
82 | stack_t ss;
83 | ss.ss_sp = signalHandlerStack = malloc(SIGSTKSZ); // Usual requirements for alternative signal stack.
84 | if (ss.ss_sp == 0) {
85 | qWarning("Warning: Could not allocate space for alternative signal stack (%s).", Q_FUNC_INFO);
86 | return;
87 | }
88 | ss.ss_size = SIGSTKSZ;
89 | ss.ss_flags = 0;
90 | if (sigaltstack(&ss, 0) == -1) {
91 | qWarning("Warning: Failed to set alternative signal stack (%s).", Q_FUNC_INFO);
92 | return;
93 | }
94 |
95 | // Install signal handler.
96 | struct sigaction sa;
97 | if (sigemptyset(&sa.sa_mask) == -1) {
98 | qWarning("Warning: Failed to empty signal set (%s).", Q_FUNC_INFO);
99 | return;
100 | }
101 | sa.sa_handler = &signalHandler;
102 | // SA_RESETHAND - Restore signal action to default after signal handler has been called.
103 | // SA_NODEFER - Don't block the signal after it was triggered (otherwise blocked signals get
104 | // inherited via fork() and execve()). Without this the signal will not be delivered to the
105 | // restarted Qt Creator.
106 | // SA_ONSTACK - Use alternative stack.
107 | sa.sa_flags = SA_RESETHAND | SA_NODEFER | SA_ONSTACK;
108 | // See "man 7 signal" for an overview of signals.
109 | // Do not add SIGPIPE here, QProcess and QTcpSocket use it.
110 | const int signalsToHandle[] = { SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGBUS, 0 };
111 | for (int i = 0; signalsToHandle[i]; ++i)
112 | if (sigaction(signalsToHandle[i], &sa, 0) == -1)
113 | qWarning("Failed to install signal handler for signal \"%s\"", strsignal(signalsToHandle[i]));
114 | }
115 |
116 | void qtMsgLog(QtMsgType type, const QMessageLogContext &context, const QString &msg)
117 | {
118 | static QFile log(logFilePath);
119 | if (!log.isOpen())
120 | log.open(QIODevice::WriteOnly | QIODevice::Text);
121 | char typeChar = ' ';
122 | switch (type) {
123 | case QtDebugMsg:
124 | typeChar = 'd';
125 | break;
126 | case QtInfoMsg:
127 | typeChar = 'i';
128 | break;
129 | case QtWarningMsg:
130 | typeChar = 'W';
131 | break;
132 | case QtCriticalMsg:
133 | typeChar = '!';
134 | break;
135 | case QtFatalMsg:
136 | typeChar = 'F';
137 | }
138 | char buf[64];
139 | qint64 ts = sinceStartup.elapsed();
140 | snprintf(buf, 64, "[%6lld.%03lld %c] ", ts / 1000, ts % 1000, typeChar);
141 | log.write(buf);
142 | if (context.function) {
143 | snprintf(buf, 64, "%s:%d: ", context.function, context.line);
144 | log.write(buf);
145 | }
146 | log.write(msg.toUtf8());
147 | log.write("\n");
148 | if (type == QtFatalMsg)
149 | abort();
150 | }
151 |
152 | static void registerTypes()
153 | {
154 | qmlRegisterType("com.theqtcompany.wlprocesslauncher", 1, 0, "ProcessLauncher");
155 | qmlRegisterType("com.theqtcompany.wlcompositor", 1, 0, "StackableItem");
156 | }
157 |
158 | static qreal highestDPR(QList &screens)
159 | {
160 | qreal ret = 0;
161 | for (const QScreen *scr : screens) {
162 | qDebug() << "Screen" << scr->name() << scr->geometry() << scr->physicalSize()
163 | << "DPI: log" << scr->logicalDotsPerInch() << "phys" << scr->physicalDotsPerInch()
164 | << "DPR" << scr->devicePixelRatio();
165 | ret = qMax(ret, scr->devicePixelRatio());
166 | }
167 | return ret;
168 | }
169 |
170 | int main(int argc, char *argv[])
171 | {
172 | sinceStartup.start();
173 | if (!qEnvironmentVariableIsSet("QT_XCB_GL_INTEGRATION"))
174 | qputenv("QT_XCB_GL_INTEGRATION", "xcb_egl"); // use xcomposite-glx if no EGL
175 | if (!qEnvironmentVariableIsSet("QT_WAYLAND_DISABLE_WINDOWDECORATION"))
176 | qputenv("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1");
177 | if (!qEnvironmentVariableIsSet("QT_LABS_CONTROLS_STYLE"))
178 | qputenv("QT_LABS_CONTROLS_STYLE", "Universal");
179 | if (!qEnvironmentVariableIsSet("QT_QPA_PLATFORMTHEME"))
180 | qputenv("QT_QPA_PLATFORMTHEME", "generic");
181 | QGuiApplication app(argc, argv);
182 | //QCoreApplication::setApplicationName("grefsen"); // defaults to name of the executable
183 | QCoreApplication::setOrganizationName("grefsen");
184 | QCoreApplication::setApplicationVersion("0.1");
185 | // app.setAttribute(Qt::AA_DisableHighDpiScaling); // better use the env variable... but that's not enough on eglfs
186 | grefsonExecutablePath = app.applicationFilePath().toLocal8Bit();
187 | grefsonPID = QCoreApplication::applicationPid();
188 | bool windowed = false;
189 |
190 | QList screens = QGuiApplication::screens();
191 | {
192 | QCommandLineParser parser;
193 | parser.setApplicationDescription("Grefsen Qt/Wayland compositor");
194 | parser.addHelpOption();
195 | parser.addVersionOption();
196 |
197 | QCommandLineOption respawnOption(QStringList() << "r" << "respawn",
198 | QCoreApplication::translate("main", "respawn grefsen after a crash"));
199 | parser.addOption(respawnOption);
200 |
201 | QCommandLineOption logFileOption(QStringList() << "l" << "log",
202 | QCoreApplication::translate("main", "redirect all debug/warning/error output to a log file"),
203 | QCoreApplication::translate("main", "file path"));
204 | parser.addOption(logFileOption);
205 |
206 | QCommandLineOption configDirOption(QStringList() << "c" << "config",
207 | QCoreApplication::translate("main", "load config files from the given directory (default is ~/.config/grefsen)"),
208 | QCoreApplication::translate("main", "directory path"));
209 | parser.addOption(configDirOption);
210 |
211 | QCommandLineOption screenOption(QStringList() << "s" << "screen",
212 | QCoreApplication::translate("main", "send output to the given screen"),
213 | QCoreApplication::translate("main", "screen"));
214 | parser.addOption(screenOption);
215 |
216 | QCommandLineOption windowOption(QStringList() << "w" << "window",
217 | QCoreApplication::translate("main", "run in a window rather than fullscreen"));
218 | parser.addOption(windowOption);
219 |
220 | parser.process(app);
221 | if (parser.isSet(respawnOption))
222 | setupSignalHandler();
223 | if (parser.isSet(configDirOption))
224 | grefsenConfigDirPath = parser.value(configDirOption);
225 | if (parser.isSet(logFileOption)) {
226 | logFilePath = parser.value(logFileOption);
227 | qInstallMessageHandler(qtMsgLog);
228 | }
229 | if (parser.isSet(screenOption)) {
230 | QStringList scrNames = parser.values(screenOption);
231 | QList keepers;
232 | foreach (QScreen *scr, screens)
233 | if (scrNames.contains(scr->name(), Qt::CaseInsensitive))
234 | keepers << scr;
235 | if (keepers.isEmpty()) {
236 | qWarning() << "None of the screens" << scrNames << "exist; available screens:";
237 | foreach (QScreen *scr, screens)
238 | qWarning() << " " << scr->name() << scr->geometry();
239 | return -1;
240 | }
241 | screens = keepers;
242 | }
243 | if (parser.isSet(windowOption))
244 | windowed = true;
245 |
246 | qreal dpr = highestDPR(screens);
247 | if (!qEnvironmentVariableIsSet("XCURSOR_SIZE")) {
248 | // QTBUG-67579: we can't have different cursor sizes on different screens
249 | // or different windows yet, but we can override globally
250 | int cursorSize = int(32 * dpr);
251 | qDebug() << "highest DPR" << dpr << "-> cursor size" << cursorSize;
252 | qputenv("XCURSOR_SIZE", QByteArray::number(cursorSize));
253 | }
254 |
255 | QFontDatabase fd;
256 | if (!fd.families().contains(QLatin1String("FontAwesome")))
257 | if (QFontDatabase::addApplicationFont(":/fonts/FontAwesome.otf"))
258 | qWarning("failed to load FontAwesome from resources");
259 | if (!fd.families().contains(QLatin1String("Manzanita")))
260 | if (QFontDatabase::addApplicationFont(":/fonts/manzanit.pfb"))
261 | qWarning("failed to load Manzanita font from resources");
262 | }
263 |
264 | registerTypes();
265 | qputenv("QT_QPA_PLATFORM", "wayland"); // not for grefsen but for child processes
266 |
267 | QQmlApplicationEngine appEngine;
268 | appEngine.addImportPath(app.applicationDirPath() + QLatin1String("/imports"));
269 | appEngine.load(QUrl("qrc:///qml/main.qml"));
270 | QObject *root = appEngine.rootObjects().first();
271 | root->setProperty("fullscreenAllowed", !windowed);
272 | appEngine.rootContext()->setContextProperty(glassPaneName,
273 | root->findChild(glassPaneName));
274 | QList windows = root->findChildren();
275 | auto windowIter = windows.begin();
276 | auto screenIter = screens.begin();
277 | while (windowIter != windows.end() && screenIter != screens.end()) {
278 | QWindow * window = *windowIter;
279 | QScreen * screen = *screenIter;
280 | window->setScreen(screen);
281 | if (windowed) {
282 | window->resize(1920, 1080);
283 | window->showNormal();
284 | } else {
285 | window->setGeometry(screen->geometry());
286 | window->showFullScreen();
287 | }
288 | ++windowIter;
289 | ++screenIter;
290 | if (windowed)
291 | break;
292 | }
293 |
294 | return app.exec();
295 | }
296 |
--------------------------------------------------------------------------------
/compositor/processlauncher.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015 The Qt Company Ltd.
4 | ** Contact: http://www.qt-project.org/legal
5 | **
6 | ** This file is free software; you can redistribute it and/or
7 | ** modify it under the terms of the GNU Lesser General Public
8 | ** License version 3 as published by the Free Software Foundation
9 | ** and appearing in the file LICENSE included in the packaging
10 | ** of this file.
11 | **
12 | ** This code is distributed in the hope that it will be useful,
13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ** GNU Lesser General Public License for more details.
16 | **
17 | ****************************************************************************/
18 |
19 | #include "processlauncher.h"
20 | #include
21 |
22 | WaylandProcessLauncher::WaylandProcessLauncher(QObject *parent)
23 | : QObject(parent)
24 | {
25 | }
26 |
27 | WaylandProcessLauncher::~WaylandProcessLauncher()
28 | {
29 | }
30 |
31 | void WaylandProcessLauncher::launch(const QString &program)
32 | {
33 | QProcess *process = new QProcess(this);
34 |
35 | QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
36 | process->setProcessEnvironment(env);
37 |
38 | connect(process, static_cast(&QProcess::finished),
39 | process, &QProcess::deleteLater);
40 | connect(process, &QProcess::errorOccurred, &QProcess::deleteLater);
41 | connect(process, &QProcess::errorOccurred, this, &WaylandProcessLauncher::onError);
42 | connect(process, &QProcess::stateChanged, this, &WaylandProcessLauncher::onStateChanged);
43 |
44 | QStringList arguments;
45 | arguments << "-platform" << "wayland";
46 | process->start(program, arguments);
47 |
48 | }
49 |
50 | void WaylandProcessLauncher::onError(QProcess::ProcessError error)
51 | {
52 | QProcess *proc = qobject_cast(sender());
53 | qWarning() << error << "from" << proc;
54 | if (proc)
55 | qWarning() << proc->readAllStandardError();
56 | }
57 |
58 | void WaylandProcessLauncher::onStateChanged(QProcess::ProcessState state)
59 | {
60 | QProcess *proc = qobject_cast(sender());
61 | qDebug() << proc << "state changed" << state;
62 | if (proc)
63 | qDebug() << proc->readAllStandardError();
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/compositor/processlauncher.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015 The Qt Company Ltd.
4 | ** Contact: http://www.qt-project.org/legal
5 | **
6 | ** This file is free software; you can redistribute it and/or
7 | ** modify it under the terms of the GNU Lesser General Public
8 | ** License version 3 as published by the Free Software Foundation
9 | ** and appearing in the file LICENSE included in the packaging
10 | ** of this file.
11 | **
12 | ** This code is distributed in the hope that it will be useful,
13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ** GNU Lesser General Public License for more details.
16 | **
17 | ****************************************************************************/
18 |
19 | #ifndef PROCESSLAUNCHER_H
20 | #define PROCESSLAUNCHER_H
21 |
22 | #include
23 | #include
24 |
25 |
26 | class WaylandProcessLauncher : public QObject
27 | {
28 | Q_OBJECT
29 |
30 | public:
31 | explicit WaylandProcessLauncher(QObject *parent = 0);
32 | ~WaylandProcessLauncher();
33 | Q_INVOKABLE void launch(const QString &program);
34 | protected slots:
35 | void onError(QProcess::ProcessError error);
36 | void onStateChanged(QProcess::ProcessState state);
37 | };
38 |
39 | #endif // PROCESSLAUNCHER_H
40 |
--------------------------------------------------------------------------------
/compositor/qml/Chrome.qml:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015 The Qt Company Ltd.
4 | ** Copyright (C) 2020 Shawn Rutledge
5 | **
6 | ** This file is free software; you can redistribute it and/or
7 | ** modify it under the terms of the GNU Lesser General Public
8 | ** License version 3 as published by the Free Software Foundation
9 | ** and appearing in the file LICENSE included in the packaging
10 | ** of this file.
11 | **
12 | ** This code is distributed in the hope that it will be useful,
13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ** GNU Lesser General Public License for more details.
16 | **
17 | ****************************************************************************/
18 |
19 | import QtQuick
20 | import QtQuick.Window
21 | import QtWayland.Compositor
22 | import QtWayland.Compositor.XdgShell
23 | import Qt5Compat.GraphicalEffects
24 | import com.theqtcompany.wlcompositor
25 |
26 | StackableItem {
27 | id: rootChrome
28 | property alias shellSurface: surfaceItem.shellSurface
29 | property var topLevel
30 | property alias moveItem: surfaceItem.moveItem
31 | property bool decorationVisible: false
32 | property bool moving: surfaceItem.moveItem ? surfaceItem.moveItem.moving : false
33 | property alias destroyAnimation : destroyAnimationImpl
34 |
35 | property int marginWidth : surfaceItem.isFullscreen ? 0 : (surfaceItem.isPopup ? 1 : 6)
36 | property int titlebarHeight : surfaceItem.isPopup || surfaceItem.isFullscreen ? 0 : 25
37 | property string screenName: ""
38 |
39 | property real resizeAreaWidth: 12
40 |
41 | x: surfaceItem.moveItem.x - surfaceItem.output.geometry.x
42 | y: surfaceItem.moveItem.y - surfaceItem.output.geometry.y
43 | height: surfaceItem.height + marginWidth + titlebarHeight
44 | width: surfaceItem.width + 2 * marginWidth
45 | visible: surfaceItem.valid
46 |
47 | Rectangle {
48 | id: decoration
49 | anchors.fill: parent
50 | border.width: 1
51 | radius: marginWidth
52 | border.color: (rightEdgeHover.hovered || bottomEdgeHover.hovered) ? "#ffc02020" :"#305070a0"
53 | color: "#50ffffff"
54 | visible: rootChrome.decorationVisible && !surfaceItem.isFullscreen &&
55 | !topLevel || topLevel.decorationMode === XdgToplevel.ServerSideDecoration
56 |
57 | // TODO write a ResizeHandler for this purpose? otherwise there are up to 8 components for edges and corners
58 | Item {
59 | id: rightEdgeResizeArea
60 | x: parent.width - resizeAreaWidth / 2; width: resizeAreaWidth; height: parent.height - resizeAreaWidth
61 | onXChanged:
62 | if (horzDragHandler.active) {
63 | var size = topLevel.sizeForResize(horzDragHandler.initialSize,
64 | Qt.point(horzDragHandler.translation.x, horzDragHandler.translation.y),
65 | Qt.RightEdge);
66 | topLevel.sendConfigure(size, [3] /*XdgShellToplevel.ResizingState*/ )
67 | }
68 | DragHandler {
69 | id: horzDragHandler
70 | property size initialSize
71 | onActiveChanged: if (active) initialSize = Qt.size(rootChrome.width, rootChrome.height)
72 | yAxis.enabled: false
73 | }
74 | HoverHandler {
75 | id: rightEdgeHover
76 | cursorShape: Qt.SizeHorCursor // problem: this so far only sets the EGLFS cursor, not WaylandCursorItem
77 | }
78 | }
79 | Item {
80 | id: bottomEdgeResizeArea
81 | y: parent.height - resizeAreaWidth / 2; height: resizeAreaWidth; width: parent.width - resizeAreaWidth
82 | onYChanged:
83 | if (vertDragHandler.active) {
84 | var size = topLevel.sizeForResize(vertDragHandler.initialSize,
85 | Qt.point(vertDragHandler.translation.x, vertDragHandler.translation.y),
86 | Qt.BottomEdge);
87 | topLevel.sendConfigure(size, [3] /*XdgShellToplevel.ResizingState*/ )
88 | }
89 | DragHandler {
90 | id: vertDragHandler
91 | property size initialSize
92 | onActiveChanged: if (active) initialSize = Qt.size(rootChrome.width, rootChrome.height)
93 | xAxis.enabled: false
94 | }
95 | HoverHandler {
96 | id: bottomEdgeHover
97 | cursorShape: Qt.SizeVerCursor
98 | }
99 | }
100 | Item {
101 | id: bottomRightResizeArea
102 | x: parent.width - resizeAreaWidth / 2; y: parent.height - resizeAreaWidth / 2
103 | width: resizeAreaWidth; height: parent.height - resizeAreaWidth
104 | onXChanged: resize()
105 | onYChanged: resize()
106 | function resize() {
107 | if (bottomRightDragHandler.active) {
108 | var size = topLevel.sizeForResize(bottomRightDragHandler.initialSize,
109 | Qt.point(bottomRightDragHandler.translation.x, bottomRightDragHandler.translation.y),
110 | Qt.BottomEdge | Qt.RightEdge);
111 | topLevel.sendConfigure(size, [3] /*XdgShellToplevel.ResizingState*/ )
112 | }
113 | }
114 | DragHandler {
115 | id: bottomRightDragHandler
116 | property size initialSize
117 | onActiveChanged: if (active) initialSize = Qt.size(rootChrome.width, rootChrome.height)
118 | }
119 | HoverHandler {
120 | id: bottomRightHover
121 | cursorShape: Qt.SizeFDiagCursor
122 | }
123 | }
124 | // end of resizing components
125 |
126 | Item {
127 | id: titlebar
128 | anchors.margins: marginWidth
129 | anchors.top: parent.top
130 | anchors.left: parent.left
131 | anchors.right: parent.right
132 | height: titlebarHeight - marginWidth
133 | visible: !surfaceItem.isPopup
134 |
135 | LinearGradient {
136 | anchors.fill: parent
137 | start: Qt.point(0, 0)
138 | end: Qt.point(0, height)
139 | gradient: Gradient {
140 | GradientStop { position: 0.0; color: "#50ffffff" }
141 | GradientStop { position: 1.0; color: "#e0ffffff" }
142 | }
143 | }
144 |
145 | Text {
146 | color: "gray"
147 | text: surfaceItem.shellSurface.title !== undefined ? surfaceItem.shellSurface.title : ""
148 | anchors.margins: marginWidth
149 |
150 | anchors.left: parent.left
151 | anchors.verticalCenter: parent.verticalCenter
152 | }
153 |
154 | DragHandler {
155 | id: titlebarDrag
156 | target: rootChrome
157 | cursorShape: Qt.ClosedHandCursor
158 | property var movingBinding: Binding {
159 | target: surfaceItem.moveItem
160 | property: "moving"
161 | value: titlebarDrag.active
162 | }
163 | }
164 |
165 | HoverHandler {
166 | cursorShape: Qt.OpenHandCursor
167 | }
168 |
169 | TapHandler {
170 | acceptedButtons: Qt.LeftButton
171 | gesturePolicy: TapHandler.DragThreshold
172 | onTapped: rootChrome.raise()
173 | }
174 |
175 | TapHandler {
176 | acceptedButtons: Qt.MiddleButton
177 | gesturePolicy: TapHandler.DragThreshold
178 | onTapped: rootChrome.lower()
179 | }
180 |
181 | RectangularGlow {
182 | id: closeButton
183 | visible: !surfaceItem.isTransient
184 | height: 8
185 | width: 8
186 | anchors.margins: marginWidth
187 | anchors.right: parent.right
188 | anchors.verticalCenter: parent.verticalCenter
189 | glowRadius: 8
190 | cornerRadius: glowRadius
191 | spread: 0.4
192 | color: closeButtonHover.hovered ? "#88FF0000" : "transparent"
193 | Text {
194 | id: closeIcon
195 | anchors.centerIn: parent
196 | font.pixelSize: parent.height + parent.glowRadius
197 | font.family: "FontAwesome"
198 | text: "\uf00d"
199 | }
200 | HoverHandler {
201 | id: closeButtonHover
202 | }
203 | TapHandler {
204 | onTapped: topLevel.sendClose()
205 | }
206 | }
207 | }
208 | }
209 |
210 | SequentialAnimation {
211 | id: destroyAnimationImpl
212 | ParallelAnimation {
213 | NumberAnimation { target: scaleTransform; property: "yScale"; to: 2/height; duration: 150 }
214 | NumberAnimation { target: scaleTransform; property: "xScale"; to: 0.4; duration: 150 }
215 | }
216 | NumberAnimation { target: scaleTransform; property: "xScale"; to: 0; duration: 150 }
217 | ScriptAction { script: { rootChrome.destroy(); } }
218 | }
219 |
220 | ParallelAnimation {
221 | id: createAnimationImpl
222 | NumberAnimation { target: scaleTransform; property: "yScale"; from: 0; to: 1; duration: 150 }
223 | NumberAnimation { target: scaleTransform; property: "xScale"; from: 0; to: 1; duration: 150 }
224 | }
225 |
226 | SequentialAnimation {
227 | id: receivedFocusAnimation
228 | ParallelAnimation {
229 | NumberAnimation { target: scaleTransform; property: "yScale"; to: 1.02; duration: 100; easing.type: Easing.OutQuad }
230 | NumberAnimation { target: scaleTransform; property: "xScale"; to: 1.02; duration: 100; easing.type: Easing.OutQuad }
231 | }
232 | ParallelAnimation {
233 | NumberAnimation { target: scaleTransform; property: "yScale"; to: 1; duration: 100; easing.type: Easing.InOutQuad }
234 | NumberAnimation { target: scaleTransform; property: "xScale"; to: 1; duration: 100; easing.type: Easing.InOutQuad }
235 | }
236 | }
237 |
238 | transform: [
239 | Scale {
240 | id:scaleTransform
241 | origin.x: rootChrome.width / 2
242 | origin.y: rootChrome.height / 2
243 |
244 | }
245 | ]
246 |
247 | ShellSurfaceItem {
248 | id: surfaceItem
249 | property bool valid: false
250 | property bool isPopup: false
251 | property bool isTransient: false
252 | property bool isFullscreen: false
253 |
254 | opacity: moving ? 0.5 : 1.0
255 | inputEventsEnabled: !pinch3.active && !metaDragHandler.active && !altDragHandler.active
256 |
257 | x: marginWidth
258 | y: titlebarHeight
259 |
260 | DragHandler {
261 | id: metaDragHandler
262 | acceptedModifiers: Qt.MetaModifier
263 | target: surfaceItem.moveItem
264 | property var movingBinding: Binding {
265 | target: surfaceItem.moveItem
266 | property: "moving"
267 | value: metaDragHandler.active
268 | }
269 | }
270 |
271 | DragHandler {
272 | id: altDragHandler
273 | acceptedModifiers: Qt.AltModifier
274 | target: surfaceItem.moveItem
275 | property var movingBinding: Binding {
276 | target: surfaceItem.moveItem
277 | property: "moving"
278 | value: altDragHandler.active
279 | }
280 | }
281 |
282 | Connections {
283 | target: shellSurface
284 | ignoreUnknownSignals: true
285 |
286 | onActivatedChanged: { // xdg_shell only
287 | if (shellSurface.activated)
288 | receivedFocusAnimation.start();
289 | }
290 | onSetPopup: {
291 | surfaceItem.isPopup = true
292 | decoration.visible = false
293 | }
294 | onSetTransient: {
295 | surfaceItem.isTransient = true
296 | }
297 | onSetFullScreen: {
298 | surfaceItem.isFullscreen = true
299 | rootChrome.x = 0
300 | rootChrome.y = 0
301 | }
302 | }
303 |
304 | onSurfaceDestroyed: {
305 | destroyAnimationImpl.start();
306 | }
307 |
308 | onWidthChanged: {
309 | valid = !surface.cursorSurface && surface.bufferSize.width > 0 && surface.bufferSize.height > 0
310 | }
311 |
312 | onValidChanged: if (valid) {
313 | if (isFullscreen) {
314 | topLevel.sendFullscreen(output.geometry)
315 | } else if (decorationVisible) {
316 | createAnimationImpl.start()
317 | }
318 | }
319 | }
320 |
321 | PinchHandler {
322 | id: pinch3
323 | objectName: "3-finger pinch"
324 | minimumPointCount: 3
325 | maximumPointCount: 3
326 | minimumScale: 0.1
327 | minimumRotation: 0
328 | maximumRotation: 0
329 | onActiveChanged: if (!active) {
330 | // just a silly way of getting a QSize for sendConfigure()
331 | var size = topLevel.sizeForResize(Qt.size(width * scale, height * scale), Qt.point(0, 0), 0);
332 | topLevel.sendConfigure(size, [3] /*XdgShellToplevel.ResizingState*/);
333 | rootChrome.scale = 1
334 | }
335 | }
336 |
337 | Rectangle {
338 | visible: moving || metaDragHandler.active || altDragHandler.active
339 | border.color: "white"
340 | color: "black"
341 | radius: 5
342 | anchors.centerIn: parent
343 | width: height * 10
344 | height: moveGeometryText.implicitHeight * 1.5
345 | Text {
346 | id: moveGeometryText
347 | color: "white"
348 | anchors.centerIn: parent
349 | text: Math.round(rootChrome.x) + "," + Math.round(rootChrome.y) + " on " + rootChrome.screenName
350 | }
351 | }
352 | }
353 |
--------------------------------------------------------------------------------
/compositor/qml/Keyboard.qml:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
4 | ** Contact: http://www.qt-project.org/legal
5 | **
6 | ** This file is free software; you can redistribute it and/or
7 | ** modify it under the terms of the GNU Lesser General Public
8 | ** License version 3 as published by the Free Software Foundation
9 | ** and appearing in the file LICENSE included in the packaging
10 | ** of this file.
11 | **
12 | ** This code is distributed in the hope that it will be useful,
13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ** GNU Lesser General Public License for more details.
16 | **
17 | ****************************************************************************/
18 |
19 | import QtQuick 2.5
20 | import QtQuick.VirtualKeyboard 2.1
21 |
22 | InputPanel {
23 | id: inputPanel
24 | visible: active
25 | y: active ? parent.height - inputPanel.height : parent.height
26 | anchors.left: parent.left
27 | anchors.right: parent.right
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/compositor/qml/Output.qml:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015 The Qt Company Ltd.
4 | ** Contact: http://www.qt-project.org/legal
5 | **
6 | ** This file is free software; you can redistribute it and/or
7 | ** modify it under the terms of the GNU Lesser General Public
8 | ** License version 3 as published by the Free Software Foundation
9 | ** and appearing in the file LICENSE included in the packaging
10 | ** of this file.
11 | **
12 | ** This code is distributed in the hope that it will be useful,
13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ** GNU Lesser General Public License for more details.
16 | **
17 | ****************************************************************************/
18 |
19 | import QtQuick 2.8
20 | import QtQuick.Window 2.3
21 | import QtWayland.Compositor 1.0
22 | import Grefsen 1.0
23 |
24 | WaylandOutput {
25 | id: output
26 | property variant viewsBySurface: ({})
27 | property alias surfaceArea: compositorArea // Chrome instances are parented to compositorArea
28 | property alias targetScreen: win.screen
29 | sizeFollowsWindow: true
30 |
31 | window: Window {
32 | id: win
33 | property Item customizedBackground: desktopLoader.item
34 | color: "black"
35 | title: "Grefsen on " + Screen.name
36 |
37 | WaylandMouseTracker {
38 | id: mouseTracker
39 | objectName: "wmt on " + Screen.name
40 | anchors.fill: parent
41 | windowSystemCursorEnabled: false
42 |
43 | Item {
44 | id: background
45 | anchors.fill: parent
46 | Loader {
47 | id: desktopLoader
48 | anchors.fill: parent
49 | source: "file://" + Env.grefsenconfig + "screen.qml"
50 | }
51 | }
52 | Item {
53 | id: compositorArea
54 | anchors.fill: parent
55 | }
56 | /*
57 | Loader {
58 | anchors.fill: parent
59 | source: "Keyboard.qml"
60 | }
61 | */
62 | Item {
63 | id: glassPane
64 | objectName: "glassPane"
65 | anchors.fill: parent
66 | }
67 | WaylandCursorItem {
68 | id: cursor
69 | x: mouseTracker.mouseX
70 | y: mouseTracker.mouseY
71 | seat: output.compositor.defaultSeat
72 | visible: mouseTracker.containsMouse
73 | }
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/compositor/qml/main.qml:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015 The Qt Company Ltd.
4 | ** Copyright (C) 2020 Shawn Rutledge
5 | **
6 | ** This file is free software; you can redistribute it and/or
7 | ** modify it under the terms of the GNU Lesser General Public
8 | ** License version 3 as published by the Free Software Foundation
9 | ** and appearing in the file LICENSE included in the packaging
10 | ** of this file.
11 | **
12 | ** This code is distributed in the hope that it will be useful,
13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ** GNU Lesser General Public License for more details.
16 | **
17 | ****************************************************************************/
18 |
19 | import QtQuick
20 | import QtWayland.Compositor
21 | import QtWayland.Compositor.XdgShell
22 | import QtWayland.Compositor.WlShell
23 | import Qt.labs.settings
24 |
25 | WaylandCompositor {
26 | id: comp
27 |
28 | Instantiator {
29 | id: screens
30 | model: Qt.application.screens
31 |
32 | delegate: Output {
33 | compositor: comp
34 | targetScreen: modelData
35 | Component.onCompleted: if (!comp.defaultOutput) comp.defaultOutput = this
36 | position: Qt.point(virtualX, virtualY)
37 | }
38 | }
39 |
40 | Component {
41 | id: chromeComponent
42 | Chrome {
43 | }
44 | }
45 |
46 | Component {
47 | id: moveItemComponent
48 | Item {
49 | property bool moving: false
50 | }
51 | }
52 |
53 | QtWindowManager {
54 | id: qtWindowManager
55 | onShowIsFullScreenChanged: console.debug("Show is fullscreen hint for Qt applications:", showIsFullScreen)
56 | }
57 |
58 | WlShell {
59 | onWlShellSurfaceCreated: handleShellSurfaceCreated(shellSurface, shellSurface, true)
60 | }
61 |
62 | XdgShell {
63 | // void toplevelCreated(QWaylandXdgToplevel *toplevel, QWaylandXdgSurface *xdgSurface);
64 | onToplevelCreated: handleShellSurfaceCreated(xdgSurface, toplevel, true)
65 | }
66 |
67 | XdgDecorationManagerV1 {
68 | preferredMode: XdgToplevel.ServerSideDecoration
69 | }
70 |
71 | TextInputManager {
72 | }
73 |
74 | defaultSeat.keymap {
75 | layout: keymapSettings.layout
76 | variant: keymapSettings.variant
77 | options: keymapSettings.options
78 | rules: keymapSettings.rules
79 | model: keymapSettings.model
80 | }
81 | Settings {
82 | id: keymapSettings
83 | category: "keymap"
84 | property string layout: "us"
85 | property string variant: "intl"
86 | property string options: "grp:shifts_toggle,compose:ralt,ctrl:nocaps"
87 | property string rules: ""
88 | property string model: ""
89 | }
90 |
91 | function createShellSurfaceItem(shellSurface, topLevel, moveItem, output, decorate) {
92 | var parentSurfaceItem = output.viewsBySurface[shellSurface.parentSurface];
93 | var parent = parentSurfaceItem || output.surfaceArea;
94 | var item = chromeComponent.createObject(parent, {
95 | "shellSurface": shellSurface,
96 | "topLevel": topLevel,
97 | "moveItem": moveItem,
98 | "output": output,
99 | "screenName": output.targetScreen.name,
100 | "decorationVisible": decorate
101 | });
102 | if (parentSurfaceItem !== undefined) {
103 | item.x += output.position.x;
104 | item.y += output.position.y;
105 | }
106 | output.viewsBySurface[shellSurface.surface] = item;
107 | console.log(lcComp, "shellSurface:", shellSurface, "topLevel:", topLevel, "moveItem:", moveItem, "output:", output,
108 | "decorate:", decorate, "parentSurfaceItem:", parentSurfaceItem, "new surface item:", output.viewsBySurface[shellSurface.surface])
109 | }
110 |
111 | function handleShellSurfaceCreated(shellSurface, topLevel, decorate) {
112 | var moveItem = moveItemComponent.createObject(defaultOutput.surfaceArea, {
113 | "x": screens.objectAt(0).position.x,
114 | "y": screens.objectAt(0).position.y,
115 | "width": Qt.binding(function() { return shellSurface.surface.width; }),
116 | "height": Qt.binding(function() { return shellSurface.surface.height; })
117 | });
118 | for (var i = 0; i < screens.count; ++i)
119 | createShellSurfaceItem(shellSurface, topLevel, moveItem, screens.objectAt(i), decorate);
120 | }
121 |
122 | LoggingCategory {
123 | id: lcComp
124 | name: "grefsen.compositor"
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/compositor/stackableitem.cpp:
--------------------------------------------------------------------------------
1 | #include "stackableitem.h"
2 |
3 | StackableItem::StackableItem()
4 | {
5 |
6 | }
7 |
8 |
9 | void StackableItem::lower()
10 | {
11 | QQuickItem *parent = parentItem();
12 | Q_ASSERT(parent);
13 | QQuickItem *bottom = parent->childItems().first();
14 | if (this != bottom)
15 | stackBefore(bottom);
16 | }
17 |
18 | void StackableItem::raise()
19 | {
20 | QQuickItem *parent = parentItem();
21 | Q_ASSERT(parent);
22 | QQuickItem *top = parent->childItems().last();
23 | if (this != top)
24 | stackAfter(top);
25 | }
26 |
--------------------------------------------------------------------------------
/compositor/stackableitem.h:
--------------------------------------------------------------------------------
1 | #ifndef STACKABLEITEM_H
2 | #define STACKABLEITEM_H
3 |
4 | #include
5 |
6 | class StackableItem : public QQuickItem
7 | {
8 | Q_OBJECT
9 | public:
10 | StackableItem();
11 |
12 | public Q_SLOTS:
13 | void raise();
14 | void lower();
15 | };
16 |
17 | #endif // STACKABLEITEM_H
18 |
--------------------------------------------------------------------------------
/example-config/example-config.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = aux
2 | OTHER_FILES = *.qml *.conf
3 |
--------------------------------------------------------------------------------
/example-config/grefsen.conf:
--------------------------------------------------------------------------------
1 | [keymap]
2 | layout="us,ru,gr"
3 | model=
4 | options="grp:shifts_toggle,compose:ralt,ctrl:nocaps"
5 | rules=
6 | variant="intl,phonetic"
7 |
--------------------------------------------------------------------------------
/example-config/screen.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.6
2 | import Grefsen 1.0
3 |
4 | Image {
5 | fillMode: Image.PreserveAspectCrop
6 | // download from https://commons.wikimedia.org/wiki/File:Oslo_mot_Grefsentoppen_fra_Ekeberg.jpg
7 | source: Env.grefsenconfig + "Oslo_mot_Grefsentoppen_fra_Ekeberg.jpg"
8 |
9 | // TODO set the icon theme
10 |
11 | LeftSlidePanel {
12 | id: leftPanel
13 |
14 | LauncherMenuIcon { }
15 |
16 | LauncherIcon {
17 | path: "/usr/bin/konsole"
18 | icon: "utilities-terminal"
19 | }
20 |
21 | LauncherIcon {
22 | path: "/usr/bin/qtcreator"
23 | icon: "QtProject-qtcreator"
24 | }
25 |
26 | LauncherIcon {
27 | // the Qt WebKit example; replace with your favorite Wayland-compatible browser
28 | path: "fancybrowser"
29 | icon: "internet-web-browser"
30 | }
31 |
32 | LauncherIcon {
33 | path: "quasselclient"
34 | icon: "quassel"
35 | }
36 |
37 | /*
38 | // depends on https://git.merproject.org/mer-core/libconnman-qt
39 | PopoverTrayIcon {
40 | popover: ConnmanPopover { }
41 | icon: "preferences-system-network"
42 | }
43 | */
44 |
45 | PanelClock { }
46 |
47 | QuitButton { }
48 |
49 | Shortcut {
50 | sequence: "Meta+A" // maybe not the best one... or maybe we don't need it at all
51 | onActivated: leftPanel.toggle()
52 | }
53 | }
54 |
55 | Shortcut {
56 | sequence: "Ctrl+Alt+Backspace"
57 | onActivated: Qt.quit()
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/grefsen.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 |
3 | SUBDIRS += compositor imports example-config thirdparty
4 |
--------------------------------------------------------------------------------
/imports/Grefsen/ConnmanPopover.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.6
2 | import QtQuick.Controls 2.0
3 | import MeeGo.Connman 0.2
4 |
5 | Popover {
6 | width: 320
7 | height: 480
8 |
9 | NetworkManager {
10 | id: manager
11 | function checkTechnologies() {
12 | var tl = manager.technologiesList()
13 | if (tl.length > 0)
14 | techList.model = tl
15 | console.log("state " + state + " available? " + available + " enabled svcs " + servicesEnabled + " techs " + technologiesEnabled)
16 | console.log(" list " + tl)
17 | }
18 |
19 | onAvailabilityChanged: checkTechnologies()
20 | onStateChanged: checkTechnologies()
21 | onOfflineModeChanged: checkTechnologies()
22 | onTechnologiesChanged: checkTechnologies()
23 | onTechnologiesEnabledChanged: checkTechnologies()
24 | }
25 |
26 | SwipeView {
27 | id: swipeView
28 |
29 | currentIndex: 0
30 | anchors.fill: parent
31 |
32 | ListView {
33 | id: techList
34 | spacing: 2
35 | delegate: Rectangle {
36 | width: parent.width
37 | height: 40
38 | color: "#222"
39 | property NetworkTechnology tech: manager.getTechnology(modelData)
40 | Text {
41 | text: tech.name
42 | color: "white"
43 | anchors.verticalCenter: parent.verticalCenter
44 | x: 6
45 | }
46 | MouseArea {
47 | anchors.fill: parent
48 | enabled: modelData === "wifi" && techEnableSwitch.checked
49 | onClicked: swipeView.currentIndex = 1
50 | }
51 | Switch {
52 | id: techEnableSwitch
53 | checked: tech.powered
54 | anchors {
55 | right: parent.right
56 | margins: 6
57 | verticalCenter: parent.verticalCenter
58 | }
59 | onReleased: {
60 | console.log("setting " + modelData + " " + !checked)
61 | tech.powered = !checked
62 | }
63 | }
64 | }
65 | }
66 | ListView {
67 | id: wifiList
68 | spacing: 2
69 | model: TechnologyModel {
70 | id: techModel
71 | name: "wifi"
72 | onAvailabilityChanged: {
73 | console.log(name + " powered? " + powered + " available? " + available)
74 | }
75 | }
76 | delegate: Rectangle {
77 | width: parent.width
78 | height: 40
79 | color: "#222"
80 | Text {
81 | text: networkService.name
82 | color: "white"
83 | anchors.verticalCenter: parent.verticalCenter
84 | x: 6
85 | }
86 | Text {
87 | anchors {
88 | right: parent.right
89 | margins: 6
90 | verticalCenter: parent.verticalCenter
91 | }
92 | color: "white"
93 | font.pixelSize: 30
94 | text: networkService.connected ? "✔" : ""
95 | }
96 | MouseArea {
97 | anchors.fill: parent
98 | enabled: !networkService.connected
99 | onClicked: {
100 | console.log("trying to connect to " + networkService.name)
101 | networkService.requestConnect()
102 | }
103 | }
104 | }
105 | }
106 | Item {
107 | id: wifiConfigPage
108 | Rectangle {
109 | anchors.fill: parent
110 | anchors.margins: 6
111 | anchors.bottomMargin: indicator.height
112 | radius: 8
113 | color: "#444"
114 | }
115 | }
116 | }
117 |
118 | PageIndicator {
119 | id: indicator
120 |
121 | count: swipeView.count
122 | currentIndex: swipeView.currentIndex
123 |
124 | anchors.bottom: swipeView.bottom
125 | anchors.horizontalCenter: parent.horizontalCenter
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/imports/Grefsen/Grefsen.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 | TARGET = grefsenplugin
3 | TARGETPATH = Grefsen
4 | QT += qml quick xml
5 | CONFIG += link_pkgconfig
6 | QMAKE_CXXFLAGS += -std=c++17
7 | PKGCONFIG += glib-2.0 Qt6Xdg
8 |
9 | SOURCES += \
10 | plugin.cpp \
11 | iconprovider.cpp \
12 | launchermodel.cpp
13 |
14 | HEADERS += \
15 | iconprovider.h \
16 | launchermodel.h
17 |
18 | OTHER_FILES += *.qml
19 |
--------------------------------------------------------------------------------
/imports/Grefsen/LauncherIcon.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.5
2 | import com.theqtcompany.wlprocesslauncher 1.0
3 |
4 | MouseArea {
5 | width: parent.width
6 | height: width
7 |
8 | ProcessLauncher {
9 | id: launcher
10 | }
11 |
12 | property string path: ""
13 | property string icon: ""
14 | Image {
15 | source: "image://icon/" + icon
16 | sourceSize.width: 64
17 | sourceSize.height: 64
18 | anchors.centerIn: parent
19 | }
20 | onClicked: if (path) launcher.launch(path); else console.log("nothing to launch")
21 | }
22 |
--------------------------------------------------------------------------------
/imports/Grefsen/LauncherMenu.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.15
2 | import QtQuick.Controls 2.15
3 | import Grefsen 1.0
4 |
5 | Item {
6 | id: root
7 | width: 200
8 | // height: Math.min(1000, list.implicitHeight)
9 | height: 500
10 | signal close
11 |
12 | HoverHandler {
13 | id: hover
14 | onHoveredChanged: if (!hovered) root.close()
15 | }
16 |
17 | Rectangle {
18 | anchors.fill: parent
19 | border.color: "black"
20 | color: "#555"
21 | radius: 10
22 | opacity: 0.5
23 | }
24 |
25 | TextField {
26 | id: searchField
27 | width: parent.width - 12
28 | x: 6; y: 6
29 | focus: true
30 | onTextChanged: LauncherModel.substringFilter = text
31 | MouseArea {
32 | height: searchField.height
33 | width: height
34 | anchors.right: parent.right
35 | anchors.verticalCenter: parent.verticalCenter
36 | onClicked: {
37 | LauncherModel.reset()
38 | searchField.text = ""
39 | }
40 | Text {
41 | anchors.centerIn: parent
42 | font.family: "FontAwesome"
43 | text: "\uf05c" // TODO more specific clear-text-field icon
44 | font.pointSize: searchField.font.pointSize * 1.25
45 | }
46 | }
47 |
48 | }
49 | ListView {
50 | id: list
51 | anchors.fill: parent
52 | anchors.topMargin: searchField.height + 6
53 | anchors.bottomMargin: 0
54 | anchors.margins: 6
55 | clip: true
56 | model: LauncherModel.applicationMenu
57 | delegate: MouseArea {
58 | width: parent.width
59 | height: 32
60 | onClicked: LauncherModel.select(modelData)
61 |
62 | Rectangle {
63 | radius: 2
64 | anchors.fill: parent
65 | anchors.margins: 1
66 | opacity: 0.35
67 | }
68 | Image {
69 | source: "image://icon/" + modelData.icon
70 | sourceSize.width: 22
71 | sourceSize.height: 22
72 | anchors.verticalCenter: parent.verticalCenter
73 | x: 4
74 | }
75 | Text {
76 | anchors.verticalCenter: parent.verticalCenter
77 | x: 30
78 | elide: Text.ElideRight
79 | text: modelData.title
80 | // Component.onCompleted: console.log(" "+ JSON.stringify(modelData))
81 | width: parent.width - x - 4
82 | }
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/imports/Grefsen/LauncherMenuIcon.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.5
2 | import Grefsen 1.0
3 |
4 | MouseArea {
5 | id: root
6 | width: parent.width
7 | height: width
8 | property bool checked: false
9 |
10 | Image {
11 | source: "qrc:///images/grefsen-logo-on-silhouette.png"
12 | anchors.centerIn: parent
13 | }
14 | onClicked: root.checked = !checked
15 |
16 | LauncherMenu {
17 | anchors.left: parent.right
18 | visible: root.checked
19 | onClose: root.checked = false
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/imports/Grefsen/LeftSlidePanel.qml:
--------------------------------------------------------------------------------
1 | import QtQuick
2 | import Qt5Compat.GraphicalEffects
3 | import Grefsen
4 |
5 | /*!
6 | A panel which slides out from the left edge of the screen.
7 |
8 | You can do that with your finger on the touchscreen, by mouseover,
9 | or by activating one of the open/close/toggle functions.
10 |
11 | Intended to be Fitts Law compliant: the content Item goes just to the
12 | screen edges, so that a button whose MouseArea goes to the top and/or left
13 | edge can be activated by slamming the mouse cursor into the edge and
14 | clicking.
15 | */
16 | Flickable {
17 | id: root
18 | width: 72
19 | height: contentContainer.implicitHeight + 20
20 | anchors.left: parent.left
21 | y: -10
22 | parent: glassPane
23 | contentWidth: width * 2
24 | contentHeight: height
25 |
26 | function open() {
27 | contentX = 0
28 | }
29 |
30 | function close() {
31 | contentX = width
32 | }
33 |
34 | function toggle() {
35 | if (contentX < width / 2)
36 | contentX = width
37 | else
38 | contentX = 0
39 | }
40 |
41 | Component.onCompleted: close()
42 |
43 | default property alias __content: contentContainer.data
44 | onDraggingChanged: if (!dragging) {
45 | if (horizontalVelocity > 0)
46 | flick(-1500, 0)
47 | else
48 | flick(1500, 0)
49 | }
50 | flickableDirection: Flickable.HorizontalFlick
51 | rebound: Transition {
52 | NumberAnimation {
53 | properties: "x"
54 | duration: 300
55 | easing.type: Easing.OutBounce
56 | }
57 | }
58 |
59 | RectangularGlow {
60 | id: effect
61 | width: root.width
62 | height: root.height
63 | glowRadius: 10
64 | spread: 0.2
65 | color: "#eaffa0"
66 | cornerRadius: rect.radius + glowRadius
67 |
68 | Rectangle {
69 | id: rect
70 | anchors.fill: parent
71 | anchors.leftMargin: -200
72 | color: "#394d65"
73 | radius: 10
74 | antialiasing: true
75 |
76 | Column {
77 | id: contentContainer
78 | anchors.fill: parent
79 | anchors.leftMargin: 200
80 | anchors.topMargin: 10
81 | anchors.margins: 6
82 | spacing: 6
83 | }
84 | }
85 | HoverHandler {
86 | margin: 10
87 | onHoveredChanged: if (hovered) root.open(); else root.close()
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/imports/Grefsen/PanelClock.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.5
2 | import QtQuick.Controls 1.4
3 | import QtQuick.Controls.Styles 1.4
4 |
5 | PopoverPanelItem {
6 |
7 | Item {
8 | id: clock
9 | height: time.implicitHeight - 8
10 | Text {
11 | x: 2
12 | id: time
13 | font.family: "Manzanita"
14 | font.pixelSize: 46
15 | color: "lightgreen"
16 | }
17 | Text {
18 | x: 48
19 | id: seconds
20 | font.family: "Manzanita"
21 | font.pixelSize: 30
22 | color: "lightgreen"
23 | anchors.baseline: time.baseline
24 | }
25 | function update() {
26 | var now = new Date();
27 | time.text = ("0" + now.getUTCHours()).slice(-2) + ":"
28 | + ("0" + now.getUTCMinutes()).slice(-2)
29 | seconds.text = ":" + ("0" + now.getUTCSeconds()).slice(-2);
30 | date.text = now.getFullYear() + "." +
31 | ("0" + (now.getMonth() + 1)).slice(-2) + "." +
32 | ("0" + now.getDate()).slice(-2)
33 | }
34 | }
35 | Text {
36 | id: date
37 | font.pixelSize: 11
38 | anchors.top: clock.bottom
39 | anchors.horizontalCenter: parent.horizontalCenter
40 | anchors.horizontalCenterOffset: 2
41 | color: "beige"
42 | }
43 | Timer {
44 | interval: 1000; running: true; repeat: true
45 | onTriggered: clock.update()
46 | }
47 | Component.onCompleted: clock.update()
48 |
49 | popover: Popover {
50 | width: 480
51 | height: 320
52 |
53 | Calendar {
54 | anchors.fill: parent
55 | frameVisible: false
56 | style: CalendarStyle {
57 | gridVisible: false
58 | background: Item { }
59 | dayDelegate: Rectangle {
60 | color: styleData.selected ? "#111" :
61 | (styleData.visibleMonth && styleData.valid ? "#444" : "#666");
62 |
63 | Text {
64 | text: styleData.date.getDate()
65 | anchors.centerIn: parent
66 | color: styleData.valid ? "beige" : "brown"
67 | font.pointSize: 14
68 | }
69 |
70 | Rectangle {
71 | width: parent.width
72 | height: 1
73 | color: "#555"
74 | anchors.bottom: parent.bottom
75 | }
76 |
77 | Rectangle {
78 | width: 1
79 | height: parent.height
80 | color: "#555"
81 | anchors.right: parent.right
82 | }
83 | }
84 | navigationBar: Rectangle {
85 | color: "beige"
86 | implicitHeight: row.implicitHeight
87 | radius: 6
88 | antialiasing: true
89 | Row {
90 | id: row
91 | anchors.fill: parent
92 | ToolButton {
93 | id: prevButton
94 | text: "\u25c0" // BLACK LEFT-POINTING TRIANGLE
95 | onClicked: control.showPreviousMonth()
96 | }
97 | Text {
98 | id: navigationBar
99 | text: styleData.title
100 | width: parent.width - prevButton.width - nextButton.width
101 | horizontalAlignment: Text.AlignHCenter
102 | anchors.verticalCenter: parent.verticalCenter
103 | font.family: "WindsorDemi"
104 | font.pointSize: 18
105 | color: "#420"
106 | }
107 | ToolButton {
108 | id: nextButton
109 | text: "\u25b6" // BLACK RIGHT-POINTING TRIANGLE
110 | onClicked: control.showNextMonth()
111 | }
112 | }
113 | }
114 | dayOfWeekDelegate: Text {
115 | id: dayLabel
116 | text: control.__locale.dayName(styleData.dayOfWeek, control.dayOfWeekFormat)
117 | horizontalAlignment: Text.AlignHCenter
118 | color: "beige"
119 | }
120 | }
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/imports/Grefsen/Popover.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.6
2 |
3 | Item {
4 | id: wrapper
5 | property real pointAboveToX: -1
6 | property real pointBelowToX: -1
7 | property real pointLeftToY: -1
8 | property real pointRightToY: -1
9 | width: 100
10 | height: 100
11 |
12 | default property alias __content: contentContainer.data
13 | property var __popoverPos: mapToItem(glassPane, x, y)
14 | Component.onCompleted: popover.parent = glassPane
15 |
16 | Item {
17 | id: popover
18 | x: __popoverPos.x
19 | y: __popoverPos.y
20 | width: wrapper.width
21 | height: wrapper.height
22 |
23 | Rectangle {
24 | id: outerBackground
25 | anchors.fill: parent
26 | color: "#555"
27 | radius: 10
28 | antialiasing: true
29 | opacity: 0.5
30 | }
31 |
32 | Rectangle {
33 | id: pointyBitBorder
34 | color: "black"
35 | border.color: innerBackground.border.color
36 | border.width: 2
37 | anchors.fill: pointyBit
38 | anchors.margins: -2
39 | rotation: 45
40 | antialiasing: true
41 | }
42 |
43 | Rectangle {
44 | id: innerBackground
45 | anchors.fill: parent
46 | anchors.margins: 4
47 | color: "#333"
48 | border.width: 2
49 | border.color: "#FF6600"
50 | radius: 10
51 | antialiasing: true
52 | }
53 |
54 | Rectangle {
55 | id: pointyBit
56 | property real pointToX: __popoverPos.x + Math.max(pointAboveToX, pointBelowToX)
57 | x: pointToX > 0 ? pointToX - popover.x - 2 : pointRightToY > 0 ? popover.width - 12 : -2
58 | property real pointToY: __popoverPos.y + Math.max(pointLeftToY, pointRightToY)
59 | y: pointToY > 0 ? pointToY - popover.y - 2 : pointBelowToX > 0 ? popover.height - 12 : -5
60 | width: 14
61 | height: 14
62 | rotation: 45
63 | color: innerBackground.color
64 | antialiasing: true
65 | visible: pointToX > 0 || pointToY > 0
66 | }
67 |
68 | Rectangle {
69 | id: contentContainer
70 | radius: 8
71 | anchors.fill: innerBackground
72 | anchors.margins: 6
73 | color: "transparent"
74 | clip: true // maybe expensive, but done because SwipeView lets neighbor panels stay visible sometimes
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/imports/Grefsen/PopoverPanelItem.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.5
2 |
3 | MouseArea {
4 | id: root
5 | width: parent.width
6 | height: width
7 |
8 | property Component popover: null
9 |
10 | Loader {
11 | id: loader
12 | anchors.left: root.right
13 | anchors.top: root.top
14 | onItemChanged: if (item) item.pointLeftToY = root.height / 2
15 | }
16 |
17 | onClicked: {
18 | if (loader.sourceComponent == undefined)
19 | loader.sourceComponent = popover
20 | else
21 | loader.sourceComponent = undefined
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/imports/Grefsen/PopoverTrayIcon.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.5
2 |
3 | PopoverPanelItem {
4 | property string icon: ""
5 | Image {
6 | source: "image://icon/" + icon
7 | sourceSize.width: 64
8 | sourceSize.height: 64
9 | anchors.centerIn: parent
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/imports/Grefsen/QuitButton.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.5
2 | import com.theqtcompany.wlprocesslauncher 1.0
3 |
4 | MouseArea {
5 | width: parent.width
6 | height: background.height
7 |
8 | Rectangle {
9 | id: background
10 | color: "red"
11 | radius: width
12 | width: height
13 | height: closeIcon.font.pixelSize * 1.1
14 | anchors.centerIn: parent
15 | }
16 |
17 | Text {
18 | id: closeIcon
19 | anchors.centerIn: parent
20 | anchors.verticalCenterOffset: -1
21 | font.pixelSize: 36
22 | font.family: "FontAwesome"
23 | text: "\uf00d"
24 | color: "white"
25 | }
26 |
27 | onClicked: Qt.quit()
28 | }
29 |
--------------------------------------------------------------------------------
/imports/Grefsen/RightSlidePanel.qml:
--------------------------------------------------------------------------------
1 | import QtQuick
2 | import Qt5Compat.GraphicalEffects
3 |
4 | Flickable {
5 | id: root
6 | width: 100
7 | height: 480
8 | anchors.right: parent.right
9 | y: -10
10 | z: 10000
11 | contentWidth: width * 2
12 | contentHeight: height
13 |
14 | function open() {
15 | contentX = width
16 | }
17 |
18 | default property alias __content: rect.data
19 | onDraggingChanged: if (!dragging) {
20 | if (horizontalVelocity > 0)
21 | flick(-1500, 0)
22 | else
23 | flick(1500, 0)
24 | }
25 | flickableDirection: Flickable.HorizontalFlick
26 | rebound: Transition {
27 | NumberAnimation {
28 | properties: "x"
29 | duration: 300
30 | easing.type: Easing.OutBounce
31 | }
32 | }
33 |
34 | RectangularGlow {
35 | id: effect
36 | x: width
37 | width: root.width
38 | height: root.height
39 | glowRadius: 10
40 | spread: 0.2
41 | color: "cyan"
42 | cornerRadius: rect.radius + glowRadius
43 |
44 | Rectangle {
45 | id: rect
46 | anchors.fill: parent
47 | anchors.rightMargin: -200
48 | color: "black"
49 | radius: 10
50 | antialiasing: true
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/imports/Grefsen/TuioTouchPanel.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.5
2 |
3 | Rectangle {
4 | color: "black"
5 | opacity: 0.8
6 | radius: 15
7 | antialiasing: true
8 | visible: false
9 |
10 | Shortcut {
11 | sequence: "Meta+T"
12 | onActivated: visible = !visible
13 | }
14 |
15 | MultiPointTouchArea {
16 | id: mpta
17 | anchors.fill: parent
18 | touchPoints: [
19 | TouchPoint { property color color: "red" },
20 | TouchPoint { property color color: "orange" },
21 | TouchPoint { property color color: "lightsteelblue" },
22 | TouchPoint { property color color: "green" },
23 | TouchPoint { property color color: "blue" },
24 | TouchPoint { property color color: "violet" },
25 | TouchPoint { property color color: "steelblue" },
26 | TouchPoint { property color color: "magenta" },
27 | TouchPoint { property color color: "goldenrod" },
28 | TouchPoint { property color color: "darkgray" }
29 | ] }
30 |
31 | Repeater {
32 | model: 10
33 |
34 | Item {
35 | id: crosshairs
36 | property TouchPoint touchPoint
37 | x: touchPoint.x - width / 2
38 | y: touchPoint.y - height / 2
39 | width: 300; height: 300
40 | visible: touchPoint.pressed
41 | rotation: touchPoint.rotation
42 |
43 | Rectangle {
44 | color: touchPoint.color
45 | anchors.centerIn: parent
46 | width: 2; height: parent.height
47 | antialiasing: true
48 | }
49 | Rectangle {
50 | color: touchPoint.color
51 | anchors.centerIn: parent
52 | width: parent.width; height: 2
53 | antialiasing: true
54 | }
55 | Rectangle {
56 | color: touchPoint.color
57 | implicitWidth: label.implicitWidth + 8
58 | implicitHeight: label.implicitHeight + 16
59 | radius: width / 2
60 | anchors.centerIn: parent
61 | antialiasing: true
62 | Rectangle {
63 | color: "black"
64 | opacity: 0.35
65 | width: (parent.width - 8) * touchPoint.pressure
66 | height: width
67 | radius: width / 2
68 | anchors.centerIn: parent
69 | antialiasing: true
70 | }
71 | Rectangle {
72 | color: "transparent"
73 | border.color: "white"
74 | border.width: 2
75 | opacity: 0.75
76 | visible: width > 0
77 | width: touchPoint.ellipseDiameters.width
78 | height: touchPoint.ellipseDiameters.height
79 | radius: Math.min(width, height) / 2
80 | anchors.centerIn: parent
81 | antialiasing: true
82 | }
83 | Text {
84 | id: label
85 | anchors.centerIn: parent
86 | color: "white"
87 | horizontalAlignment: Text.AlignHCenter
88 | verticalAlignment: Text.AlignVCenter
89 | property string uid: touchPoint.uniqueId === undefined || touchPoint.uniqueId.numericId === -1 ?
90 | "" : "\nUID " + touchPoint.uniqueId.numericId
91 | text: "x " + touchPoint.x.toFixed(1) +
92 | "\ny " + touchPoint.y.toFixed(1) + uid +
93 | "\nID " + touchPoint.pointId.toString(16) +
94 | "\n∡" + touchPoint.rotation.toFixed(1) + "°"
95 | }
96 | }
97 | Rectangle {
98 | id: velocityVector
99 | visible: width > 0
100 | width: touchPoint.velocity.length()
101 | height: 4
102 | Behavior on width { SmoothedAnimation { duration: 200 } }
103 | radius: height / 2
104 | antialiasing: true
105 | color: "gray"
106 | x: crosshairs.width / 2
107 | y: crosshairs.height / 2
108 | rotation: width > 0 ? Math.atan2(touchPoint.velocity.y, touchPoint.velocity.x) * 180 / Math.PI - crosshairs.rotation : 0
109 | Behavior on rotation { SmoothedAnimation { duration: 20 } }
110 | transformOrigin: Item.BottomLeft
111 | }
112 |
113 | Component.onCompleted: touchPoint = mpta.touchPoints[index]
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/imports/Grefsen/iconprovider.cpp:
--------------------------------------------------------------------------------
1 | #include "iconprovider.h"
2 | #include
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | static QDir UsrSharePixmaps("/usr/share/pixmaps");
9 |
10 | IconProvider::IconProvider()
11 | : QQuickImageProvider(QQuickImageProvider::Pixmap)
12 | {
13 | XdgIcon::setThemeName(QStringLiteral("oxygen")); // TODO make configurable
14 | qDebug() << "theme is" << QIcon::themeName() << "paths" << QIcon::themeSearchPaths()
15 | << "default icon" << XdgIcon::defaultApplicationIconName();
16 | }
17 |
18 | QPixmap IconProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
19 | {
20 | //qDebug() << id << requestedSize;
21 | // absolute path: just load the image
22 | if (id.startsWith('/')) {
23 | QPixmap ret(id);
24 | if (ret.width() > requestedSize.width() || ret.height() > requestedSize.height())
25 | ret = ret.scaled(requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
26 | if (size)
27 | *size = ret.size();
28 | return ret;
29 | }
30 | // main strategy: QIcon usually knows how to find it
31 | QIcon icon = QIcon::fromTheme(id);
32 | // QIcon icon = XdgIcon::fromTheme(id, XdgIcon::defaultApplicationIcon()); // often not working well
33 | // fall back to /usr/share/pixmaps if nothing found yet
34 | if (icon.isNull()) {
35 | QStringList p = UsrSharePixmaps.entryList({id + "*"}, QDir::Files);
36 | if (!p.isEmpty()) {
37 | QPixmap ret(UsrSharePixmaps.filePath(p.first()));
38 | if (ret.width() > requestedSize.width() || ret.height() > requestedSize.height())
39 | ret = ret.scaled(requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
40 | if (size)
41 | *size = ret.size();
42 | return ret;
43 | }
44 | }
45 | // default app icon if all else fails
46 | if (icon.isNull()) {
47 | qWarning() << "failed to find icon" << id;
48 | icon = QIcon::fromTheme(XdgIcon::defaultApplicationIconName());
49 | }
50 | QPixmap ret = icon.pixmap(requestedSize);
51 | if (size)
52 | *size = ret.size();
53 | return ret;
54 | }
55 |
--------------------------------------------------------------------------------
/imports/Grefsen/iconprovider.h:
--------------------------------------------------------------------------------
1 | #ifndef ICONPROVIDER_H
2 | #define ICONPROVIDER_H
3 |
4 | #include
5 | #include
6 |
7 | class IconProvider : public QQuickImageProvider
8 | {
9 | public:
10 | IconProvider();
11 |
12 | QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) Q_DECL_OVERRIDE;
13 | protected:
14 | // QDir m_usrSharePixmaps = QDir("/usr/share/pixmaps");
15 | };
16 |
17 | #endif // ICONPROVIDER_H
18 |
--------------------------------------------------------------------------------
/imports/Grefsen/launchermodel.cpp:
--------------------------------------------------------------------------------
1 | #include "launchermodel.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | LauncherModel::LauncherModel(QJSEngine *engine, QObject *parent)
9 | : QObject(parent)
10 | , m_engine(engine)
11 | , m_root(engine->newObject())
12 | , m_allApps(engine->newArray())
13 | {
14 | QString menuFile = XdgMenu::getMenuFileName();
15 | XdgMenu xdgMenu;
16 | xdgMenu.setLogDir("/tmp");
17 | xdgMenu.setEnvironments(QStringList() << "X-GREFSEN" << "Grefsen"); // TODO what's that for?
18 | // xdgMenu.setEnvironments(QStringList() << "X-LXQT" << "LXQt");
19 | bool res = xdgMenu.read(menuFile);
20 | if (!res)
21 | qWarning() << "Parse error" << xdgMenu.errorString();
22 | m_dom = xdgMenu.xml().documentElement();
23 | qDebug() << "read XML:" << menuFile << xdgMenu.menuFileName() << m_dom.tagName();
24 |
25 | build(m_root, m_dom);
26 | m_list = m_root;
27 | }
28 |
29 | void LauncherModel::setSubstringFilter(QString substringFilter)
30 | {
31 | if (m_substringFilter == substringFilter)
32 | return;
33 |
34 | m_substringFilter = substringFilter;
35 | emit substringFilterChanged();
36 |
37 | if (m_substringFilter.isEmpty()) {
38 | reset();
39 | return;
40 | }
41 | m_list = m_engine->newObject();
42 | m_list.setProperty(QStringLiteral("items"), findSubstring(QStringLiteral("title"), m_substringFilter));
43 | emit applicationsChanged();
44 | }
45 |
46 | void LauncherModel::reset()
47 | {
48 | m_list = m_root;
49 | emit applicationsChanged();
50 | }
51 |
52 | void LauncherModel::select(QJSValue sel)
53 | {
54 | QString title = sel.property(QStringLiteral("title")).toString();
55 | qDebug() << title;
56 | if (sel.hasOwnProperty(QStringLiteral("exec"))) {
57 | //qDebug() << "exec" << sel.property(QStringLiteral("exec")).toString();
58 | exec(sel.property(QStringLiteral("desktopFile")).toString());
59 | reset();
60 | } else {
61 | openSubmenu(title);
62 | }
63 | }
64 |
65 | void LauncherModel::exec(QString desktopFilePath)
66 | {
67 | XdgDesktopFile dtf;
68 | dtf.load(desktopFilePath);
69 | //qDebug() << desktopFilePath << dtf;
70 | if (dtf.isValid()) {
71 | bool ok = dtf.startDetached();
72 | if (Q_UNLIKELY(!ok))
73 | emit execFailed(tr("failed to exec '%s'", dtf.value(QStringLiteral("exec")).toString().toLocal8Bit().constData()));
74 | } else {
75 | emit execFailed(tr("failed to load desktop file '%s'", desktopFilePath.toLocal8Bit().constData()));
76 | }
77 | }
78 |
79 | void LauncherModel::openSubmenu(QString title)
80 | {
81 | m_list = findFirst(QStringLiteral("title"), title, applicationMenu());
82 | emit applicationsChanged();
83 | }
84 |
85 | void LauncherModel::build(QJSValue in, const QDomElement &xml)
86 | {
87 | QJSValue array = m_engine->newArray();
88 | in.setProperty(QStringLiteral("items"), array);
89 | DomElementIterator it(xml, QString());
90 | while(it.hasNext())
91 | {
92 | QDomElement xml = it.next();
93 |
94 | if (xml.tagName() == "Menu")
95 | appendMenu(array, xml);
96 |
97 | else if (xml.tagName() == "AppLink")
98 | appendApp(array, xml);
99 |
100 | else if (xml.tagName() == "Separator")
101 | qDebug() << "separator";
102 | }
103 | }
104 |
105 | void LauncherModel::appendMenu(QJSValue in, const QDomElement &xml)
106 | {
107 | QString title = xml.attribute(QStringLiteral("title"));
108 | QString icon = xml.attribute(QStringLiteral("icon"));
109 | int idx = in.property(QStringLiteral("length")).toInt();
110 | // qDebug() << in.property(QStringLiteral("title")).toString() << ":" << title << icon << idx;
111 | QJSValue menu = m_engine->newObject();
112 | menu.setProperty(QStringLiteral("title"), title);
113 | menu.setProperty(QStringLiteral("icon"), icon);
114 | in.setProperty(idx, menu);
115 | build(menu, xml);
116 | }
117 |
118 | void LauncherModel::appendApp(QJSValue in, const QDomElement &xml)
119 | {
120 | QString title = xml.attribute(QStringLiteral("title"));
121 | QString icon = xml.attribute(QStringLiteral("icon"));
122 | int idx = in.property(QStringLiteral("length")).toInt();
123 | // qDebug() << in.property(QStringLiteral("title")).toString() << ":" << title << icon << idx;
124 | QJSValue item = m_engine->newObject();
125 | item.setProperty(QStringLiteral("title"), title);
126 | item.setProperty(QStringLiteral("icon"), icon);
127 | item.setProperty(QStringLiteral("exec"), xml.attribute(QStringLiteral("exec")));
128 | item.setProperty(QStringLiteral("desktopFile"), xml.attribute(QStringLiteral("desktopFile")));
129 | in.setProperty(idx, item);
130 | m_allApps.setProperty(m_allAppsCount++, item);
131 | }
132 |
133 | QJSValue LauncherModel::findFirst(QString key, QString value, QJSValue array)
134 | {
135 | int i = 0;
136 | QJSValue next = array.property(i);
137 | while (!next.isUndefined()) {
138 | if (next.property(key).toString() == value)
139 | return next;
140 | next = array.property(++i);
141 | }
142 | return QJSValue();
143 | }
144 |
145 | QJSValue LauncherModel::findSubstring(QString key, QString substr)
146 | {
147 | QJSValue ret = m_engine->newArray();
148 | int i = 0, ri = 0;
149 | QJSValue next = m_allApps.property(i);
150 | while (!next.isUndefined()) {
151 | if (next.property(key).toString().contains(substr, Qt::CaseInsensitive))
152 | ret.setProperty(ri++, next);
153 | next = m_allApps.property(++i);
154 | }
155 | return ret;
156 | }
157 |
--------------------------------------------------------------------------------
/imports/Grefsen/launchermodel.h:
--------------------------------------------------------------------------------
1 | #ifndef LAUNCHERMODEL_H
2 | #define LAUNCHERMODEL_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class XdgDesktopFile;
9 |
10 | class LauncherModel : public QObject
11 | {
12 | Q_OBJECT
13 | Q_PROPERTY(QJSValue allApplications READ allApplications NOTIFY applicationsChanged)
14 | Q_PROPERTY(QJSValue applicationMenu READ applicationMenu NOTIFY applicationsChanged)
15 | Q_PROPERTY(QString substringFilter READ substringFilter WRITE setSubstringFilter NOTIFY substringFilterChanged)
16 |
17 |
18 | public:
19 | explicit LauncherModel(QJSEngine *engine, QObject *parent = 0);
20 | QJSValue allApplications() { return m_allApps; }
21 | QJSValue applicationMenu() { return m_list.property(QStringLiteral("items")); }
22 |
23 | QString substringFilter() const { return m_substringFilter; }
24 | void setSubstringFilter(QString substringFilter);
25 |
26 | signals:
27 | void applicationsChanged();
28 | void substringFilterChanged();
29 | void execFailed(QString error);
30 |
31 | public slots:
32 | void reset();
33 | void select(QJSValue sel);
34 | void exec(QString desktopFilePath);
35 | void openSubmenu(QString title);
36 |
37 |
38 | protected:
39 | void build(QJSValue in, const QDomElement& xml);
40 | void appendMenu(QJSValue in, const QDomElement& xml);
41 | void appendApp(QJSValue in, const QDomElement &xml);
42 | QJSValue findFirst(QString key, QString value, QJSValue array);
43 | QJSValue findSubstring(QString key, QString substr);
44 |
45 | protected:
46 | // static QList m_allFiles;
47 | QJSEngine *m_engine;
48 | QDomElement m_dom;
49 | QJSValue m_root;
50 | QJSValue m_list;
51 | QJSValue m_allApps;
52 | int m_allAppsCount = 0;
53 | QString m_substringFilter;
54 | };
55 |
56 | #endif // LAUNCHERMODEL_H
57 |
--------------------------------------------------------------------------------
/imports/Grefsen/plugin.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #include "iconprovider.h"
7 | #include "launchermodel.h"
8 |
9 | Q_LOGGING_CATEGORY(lcRegistration, "grefsen.registration")
10 |
11 | static const char *ModuleName = "Grefsen";
12 | static QJSValue launcherModelSingleton;
13 |
14 | static QString ensureFinalSlash(const QString &path)
15 | {
16 | if (path.endsWith('/'))
17 | return path;
18 | return path + QLatin1String("/");
19 | }
20 |
21 | static QJSValue environmentSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
22 | {
23 | Q_UNUSED(engine)
24 |
25 | QJSValue v = scriptEngine->newObject();
26 | v.setProperty(QLatin1String("home"), QDir::homePath() + "/");
27 | const QStringList &args = QCoreApplication::arguments();
28 | int argi = args.indexOf(QLatin1String("-c"));
29 | if (argi > 0 && args.length() > argi + 1)
30 | v.setProperty(QLatin1String("grefsenconfig"), ensureFinalSlash(args[argi + 1]));
31 | else
32 | v.setProperty(QLatin1String("grefsenconfig"), QDir::homePath() + "/.config/grefsen/");
33 | return v;
34 | }
35 |
36 | static QJSValue launcherModelSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
37 | {
38 | Q_UNUSED(engine)
39 |
40 | if (launcherModelSingleton.isUndefined())
41 | launcherModelSingleton = scriptEngine->newQObject(new LauncherModel(scriptEngine));
42 | return launcherModelSingleton;
43 | }
44 |
45 | class GrefsenPlugin : public QQmlExtensionPlugin
46 | {
47 | Q_OBJECT
48 | Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0")
49 |
50 | public:
51 | GrefsenPlugin() : QQmlExtensionPlugin() { }
52 |
53 | virtual void initializeEngine(QQmlEngine *engine, const char * uri) {
54 | Q_UNUSED(engine)
55 | qCDebug(lcRegistration) << uri;
56 | engine->addImageProvider(QLatin1String("icon"), new IconProvider);
57 | }
58 |
59 | virtual void registerTypes(const char *uri) {
60 | qCDebug(lcRegistration) << uri;
61 | Q_ASSERT(uri == QLatin1String(ModuleName));
62 | qmlRegisterSingletonType(ModuleName, 1, 0, "Env", environmentSingletonProvider);
63 | qmlRegisterSingletonType(ModuleName, 1, 0, "LauncherModel", launcherModelSingletonProvider);
64 | }
65 | };
66 |
67 | QT_END_NAMESPACE
68 |
69 | #include "plugin.moc"
70 |
--------------------------------------------------------------------------------
/imports/Grefsen/qmldir:
--------------------------------------------------------------------------------
1 | module Grefsen
2 | plugin grefsenplugin
3 | classname GrefsenPlugin
4 | typeinfo plugins.qmltypes
5 | ConnmanPopover 1.0 ConnmanPopover.qml
6 | LauncherIcon 1.0 LauncherIcon.qml
7 | LauncherMenu 1.0 LauncherMenu.qml
8 | LauncherMenuIcon 1.0 LauncherMenuIcon.qml
9 | LeftSlidePanel 1.0 LeftSlidePanel.qml
10 | PanelClock 1.0 PanelClock.qml
11 | Popover 1.0 Popover.qml
12 | PopoverTrayIcon 1.0 PopoverTrayIcon.qml
13 | PopoverPanelItem 1.0 PopoverPanelItem.qml
14 | QuitButton 1.0 QuitButton.qml
15 | RightSlidePanel 1.0 RightSlidePanel.qml
16 | TuioTouchPanel 1.0 TuioTouchPanel.qml
17 |
--------------------------------------------------------------------------------
/imports/imports.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 |
3 | SUBDIRS += Grefsen
4 |
--------------------------------------------------------------------------------
/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ec1oud/grefsen/05a6203a2210b57c366f36297b82c548e5653ddb/screenshot.jpg
--------------------------------------------------------------------------------
/thirdparty/freefont/README:
--------------------------------------------------------------------------------
1 | These are from a very old sunsite package which can now be found at
2 | ftp://ftp.ibiblio.org/pub/linux/X11/fonts/freefonts-0.10.tar.gz
3 |
--------------------------------------------------------------------------------
/thirdparty/freefont/manzanit.license:
--------------------------------------------------------------------------------
1 | Manzanita is a font with a victorian wood type feel Type 1 and ATM
2 | compatible. This one is free and a gift! I enjoy the design process. Hope
3 | you will look for more fonts The price will be $25.00 each.Look for more
4 | or write for a catalog. Stuffit classic file.
5 | Marty Bee, Rt. 5 Box 1720, Sulphur, LA 70663
6 |
--------------------------------------------------------------------------------
/thirdparty/freefont/manzanit.pfb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ec1oud/grefsen/05a6203a2210b57c366f36297b82c548e5653ddb/thirdparty/freefont/manzanit.pfb
--------------------------------------------------------------------------------
/thirdparty/thirdparty.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 |
3 | SUBDIRS += qt-tuio-sender
4 |
--------------------------------------------------------------------------------