add: base project
This commit is contained in:
commit
f0cb465c85
14 changed files with 1571 additions and 0 deletions
1
.direnv
Normal file
1
.direnv
Normal file
|
@ -0,0 +1 @@
|
|||
use nix
|
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
build/
|
||||
.vscode/
|
||||
debug/
|
||||
release/
|
||||
.qmake.stash
|
||||
Makefile
|
||||
Makefile.Debug
|
||||
Makefile.Release
|
35
CMakeLists.txt
Normal file
35
CMakeLists.txt
Normal file
|
@ -0,0 +1,35 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
project(waydock CXX)
|
||||
include_directories(inc)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
|
||||
file(GLOB_RECURSE SOURCES src/*.cpp)
|
||||
file(GLOB_RECURSE UI_SRC ui/*.ui)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Gui Core)
|
||||
qt_add_executable(waydock ${SOURCES} ${UI_SRC})
|
||||
qt_standard_project_setup()
|
||||
|
||||
target_link_libraries(waydock PRIVATE Widget Gui Core)
|
||||
|
||||
set_target_properties(waydock PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTOUIC ON
|
||||
AUTORCC ON
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(waydock PROPERTIES WIN32_EXECUTABLE ON)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(waydock PRIVATE "-Zc:__cplusplus" "-permissive-")
|
||||
endif()
|
||||
|
||||
if (CMAKE_BUILD_TYPE MATCHES "^[Dd]ebug")
|
||||
message(STATUS "Debug mode enabled")
|
||||
# target_compile_options(waydock PRIVATE -fsanitize=address -fno-omit-frame-pointer)
|
||||
# target_link_options(waydock PRIVATE -fsanitize=address)
|
||||
endif()
|
3
CONTRIBUTING.md
Normal file
3
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Contributing
|
||||
|
||||
To be written.
|
360
LICENSE
Normal file
360
LICENSE
Normal file
|
@ -0,0 +1,360 @@
|
|||
# GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
<https://fsf.org/>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
## Preamble
|
||||
|
||||
The licenses for most software are designed to take away your freedom
|
||||
to share and change it. By contrast, the GNU General Public License is
|
||||
intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if
|
||||
you distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on,
|
||||
we want its recipients to know that what they have is not the
|
||||
original, so that any problems introduced by others will not reflect
|
||||
on the original authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at
|
||||
all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
## TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
**0.** This License applies to any program or other work which
|
||||
contains a notice placed by the copyright holder saying it may be
|
||||
distributed under the terms of this General Public License. The
|
||||
"Program", below, refers to any such program or work, and a "work
|
||||
based on the Program" means either the Program or any derivative work
|
||||
under copyright law: that is to say, a work containing the Program or
|
||||
a portion of it, either verbatim or with modifications and/or
|
||||
translated into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".) Each licensee
|
||||
is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the Program
|
||||
(independent of having been made by running the Program). Whether that
|
||||
is true depends on what the Program does.
|
||||
|
||||
**1.** You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
**2.** You may modify your copy or copies of the Program or any
|
||||
portion of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
|
||||
**a)** You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
|
||||
**b)** You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any part
|
||||
thereof, to be licensed as a whole at no charge to all third parties
|
||||
under the terms of this License.
|
||||
|
||||
|
||||
**c)** If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such interactive
|
||||
use in the most ordinary way, to print or display an announcement
|
||||
including an appropriate copyright notice and a notice that there is
|
||||
no warranty (or else, saying that you provide a warranty) and that
|
||||
users may redistribute the program under these conditions, and telling
|
||||
the user how to view a copy of this License. (Exception: if the
|
||||
Program itself is interactive but does not normally print such an
|
||||
announcement, your work based on the Program is not required to print
|
||||
an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
**3.** You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
|
||||
**a)** Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections 1
|
||||
and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
|
||||
**b)** Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your cost of
|
||||
physically performing source distribution, a complete machine-readable
|
||||
copy of the corresponding source code, to be distributed under the
|
||||
terms of Sections 1 and 2 above on a medium customarily used for
|
||||
software interchange; or,
|
||||
|
||||
|
||||
**c)** Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is allowed
|
||||
only for noncommercial distribution and only if you received the
|
||||
program in object code or executable form with such an offer, in
|
||||
accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
**4.** You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt otherwise
|
||||
to copy, modify, sublicense or distribute the Program is void, and
|
||||
will automatically terminate your rights under this License. However,
|
||||
parties who have received copies, or rights, from you under this
|
||||
License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
**5.** You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
**6.** Each time you redistribute the Program (or any work based on
|
||||
the Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
**7.** If, as a consequence of a court judgment or allegation of
|
||||
patent infringement or for any other reason (not limited to patent
|
||||
issues), conditions are imposed on you (whether by court order,
|
||||
agreement or otherwise) that contradict the conditions of this
|
||||
License, they do not excuse you from the conditions of this License.
|
||||
If you cannot distribute so as to satisfy simultaneously your
|
||||
obligations under this License and any other pertinent obligations,
|
||||
then as a consequence you may not distribute the Program at all. For
|
||||
example, if a patent license would not permit royalty-free
|
||||
redistribution of the Program by all those who receive copies directly
|
||||
or indirectly through you, then the only way you could satisfy both it
|
||||
and this License would be to refrain entirely from distribution of the
|
||||
Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
**8.** If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
**9.** The Free Software Foundation may publish revised and/or new
|
||||
versions of the General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Program does not specify a
|
||||
version number of this License, you may choose any version ever
|
||||
published by the Free Software Foundation.
|
||||
|
||||
**10.** If you wish to incorporate parts of the Program into other
|
||||
free programs whose distribution conditions are different, write to
|
||||
the author to ask for permission. For software which is copyrighted by
|
||||
the Free Software Foundation, write to the Free Software Foundation;
|
||||
we sometimes make exceptions for this. Our decision will be guided by
|
||||
the two goals of preserving the free status of all derivatives of our
|
||||
free software and of promoting the sharing and reuse of software
|
||||
generally.
|
||||
|
||||
**NO WARRANTY**
|
||||
|
||||
**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
## How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these
|
||||
terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest to
|
||||
attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
one line to give the program's name and an idea of what it does.
|
||||
Copyright (C) yyyy name of author
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper
|
||||
mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
|
||||
type `show w'. This is free software, and you are welcome
|
||||
to redistribute it under certain conditions; type `show c'
|
||||
for details.
|
||||
|
||||
The hypothetical commands \`show w' and \`show c' should show the
|
||||
appropriate parts of the General Public License. Of course, the
|
||||
commands you use may be called something other than \`show w' and
|
||||
\`show c'; they could even be mouse-clicks or menu items--whatever
|
||||
suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or
|
||||
your school, if any, to sign a "copyright disclaimer" for the program,
|
||||
if necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright
|
||||
interest in the program `Gnomovision'
|
||||
(which makes passes at compilers) written
|
||||
by James Hacker.
|
||||
|
||||
signature of Moe Ghoul, 1 April 1989
|
||||
Moe Ghoul, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library,
|
||||
you may consider it more useful to permit linking proprietary
|
||||
applications with the library. If this is what you want to do, use the
|
||||
[GNU Lesser General Public
|
||||
License](https://www.gnu.org/licenses/lgpl.html) instead of this
|
||||
License.
|
23
README.md
Normal file
23
README.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!-- markdownlint-disable MD033 MD041 -->
|
||||
|
||||
<div align="center">
|
||||
<h1>🍭 Waydock</h1>
|
||||
<p>Universal dock for wayland compositors</p>
|
||||
<img src="https://img.shields.io/badge/C++-%2300599C.svg?logo=c%2B%2B&logoColor=white" alt="C++"/>
|
||||
<img src="https://img.shields.io/badge/Linux-FCC624?logo=linux&logoColor=black" alt="Linux"/>
|
||||
</div>
|
||||
<hr>
|
||||
Waydock is a univeral dock for wayland compositors, utilizing Qt, with a high-level of customization available.
|
||||
|
||||
## Installing
|
||||
|
||||
🛠️ TBW
|
||||
|
||||
## Configuration
|
||||
|
||||
🛠️ TBW
|
||||
|
||||
## Code of Conduct and Contributing
|
||||
|
||||
- See [CODE_OF_CONDUCT.md](https://git.lilyvex.dev/nonsensical-dev/governance/-/blob/main/CODE_OF_CONDUCT.md)
|
||||
- See [CONTRIBUTING.md](CONTRIBUTING.md)
|
24
flake.nix
Normal file
24
flake.nix
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
description = "Universal dock for wayland compositors"
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
};
|
||||
|
||||
outputs = inputs@{ flake-parts, ... }:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
systems = [
|
||||
"x86_64-linux" "aarch64-linux"
|
||||
];
|
||||
|
||||
perSystem = { config, self', inputs', pkgs, system, ... }: {
|
||||
devShells.default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
cmake
|
||||
llvmPackages_19.clangUseLLVM
|
||||
qt6.full
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
18
src/config.h
Normal file
18
src/config.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
#include "ini.h"
|
||||
#include "xdg.h"
|
||||
|
||||
class Config {
|
||||
private:
|
||||
struct config {
|
||||
std::optional<std::filesystem::path> configDir;
|
||||
|
||||
config() {}
|
||||
~config() {}
|
||||
};
|
||||
public:
|
||||
Config();
|
||||
~Config();
|
||||
};
|
47
src/dock.cpp
Normal file
47
src/dock.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include "dock.h"
|
||||
|
||||
/// @brief Create dock windows on all screens
|
||||
/// @param app Qt Application callback
|
||||
void Dock::createDockWindows(QApplication* app) {
|
||||
for (int i = 0; i < this->m_screens.size(); ++i) {
|
||||
QScreen* screen = this->m_screens[i];
|
||||
|
||||
qDebug() << "Creating dock window for screen:" << screen->name();
|
||||
|
||||
QWidget* container = new QWidget(nullptr, Qt::FramelessWindowHint | Qt::Tool);
|
||||
container->setAttribute(Qt::WA_TranslucentBackground);
|
||||
container->setAttribute(Qt::WA_NoSystemBackground);
|
||||
container->setWindowFlag(Qt::WindowStaysOnTopHint);
|
||||
container->resize(screen->geometry().size().width() / 2, 60);
|
||||
|
||||
QLabel* label = new QLabel(QString("✨ Dock %1 ✨").arg(i));
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
label->setStyleSheet(R"(
|
||||
QLabel {
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
background-color: rgba(0, 0, 0, 180);
|
||||
border-radius: 10px;
|
||||
}
|
||||
)");
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(container);
|
||||
layout->addWidget(label);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
QRect screenRect = screen->geometry();
|
||||
int x = screenRect.center().x() - container->width() / 2;
|
||||
int y = screenRect.bottom() - container->height();
|
||||
container->move(x, y);
|
||||
|
||||
container->show();
|
||||
}
|
||||
|
||||
app->exec();
|
||||
}
|
||||
|
||||
Dock::Dock(QApplication* app) {
|
||||
this->createDockWindows(app);
|
||||
}
|
||||
|
||||
Dock::~Dock() {};
|
125
src/dock.h
Normal file
125
src/dock.h
Normal file
|
@ -0,0 +1,125 @@
|
|||
#include <QApplication>
|
||||
#include <QScreen>
|
||||
#include <QRect>
|
||||
#include <QList>
|
||||
#include <QWindow>
|
||||
#include <QWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QTimer>
|
||||
#include <QLabel>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <format>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
class Dock {
|
||||
private:
|
||||
/// @brief Stores all dock windows
|
||||
QList<QWindow*> m_dockWindows;
|
||||
|
||||
/// @brief QWidget container for dock windows
|
||||
QList<QWidget*> m_dockWindowContainters;
|
||||
|
||||
/// @brief Stores all configuration windows
|
||||
QList<QWindow*> m_configWindows;
|
||||
|
||||
/// @brief Stores all available screens
|
||||
QList<QScreen*> m_screens = QGuiApplication::screens();
|
||||
|
||||
/// @brief An application in the dock
|
||||
struct dock_application {
|
||||
/// @brief Whether the application is pinned to the dock
|
||||
bool pinned;
|
||||
|
||||
/// @brief Index of where the application is pinned (0 when disabled)
|
||||
int pinIndex;
|
||||
|
||||
/// @brief Whether the application is running
|
||||
bool running;
|
||||
|
||||
/// @brief Path to the application's desktop file entry
|
||||
std::filesystem::path desktopFilePath;
|
||||
|
||||
/// @brief Reference the to the icon file of the application
|
||||
std::ifstream desktopIcon;
|
||||
|
||||
/// @brief Name of the application as specified by the desktop file
|
||||
std::string desktopName;
|
||||
|
||||
/// @brief Name of the running process, used if an application desktop file does not exist
|
||||
std::string processName;
|
||||
|
||||
/// @brief Create a new `dock_application` with an optional desktop file
|
||||
/// @param filename Optional desktop file for metadata
|
||||
dock_application() {
|
||||
getDesktopName();
|
||||
getDesktopIcon();
|
||||
}
|
||||
|
||||
/// @brief Clean up `dock_application` resources
|
||||
~dock_application() {
|
||||
if (desktopIcon.is_open()) {
|
||||
desktopIcon.close();
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Get application desktop name from desktop file
|
||||
/// @return UTF-16 desktop name
|
||||
std::optional<std::string> getDesktopName() {
|
||||
inih::INIReader r;
|
||||
std::string name;
|
||||
|
||||
try {
|
||||
r = std::move(inih::INIReader(desktopFilePath.string()));
|
||||
} catch (std::runtime_error&) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
try {
|
||||
name = r.Get<std::string>("Desktop Entry", "name");
|
||||
} catch (std::runtime_error&) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/// @brief Get application desktop icon from desktop file
|
||||
/// @return Desktop icon file stream
|
||||
std::optional<std::ifstream> getDesktopIcon() {
|
||||
inih::INIReader r;
|
||||
std::filesystem::path icon_path;
|
||||
std::ifstream icon{icon_path};
|
||||
|
||||
try {
|
||||
r = std::move(inih::INIReader(desktopFilePath.string()));
|
||||
} catch (std::runtime_error&) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
try {
|
||||
icon_path = r.Get<std::string>("Desktop Entry", "icon");
|
||||
} catch (std::runtime_error&) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief List of all applications in the dock
|
||||
std::vector<dock_application> m_dockApplications;
|
||||
|
||||
/// @brief Create dock windows on all screens
|
||||
/// @param app Qt Application callback
|
||||
void createDockWindows(QApplication* app);
|
||||
public:
|
||||
Dock(QApplication* app);
|
||||
~Dock();
|
||||
};
|
624
src/ini.h
Normal file
624
src/ini.h
Normal file
|
@ -0,0 +1,624 @@
|
|||
/**
|
||||
* Yet another .ini parser for modern c++ (made for cpp17), inspired and extend
|
||||
* from @benhoyt's inih. See project page: https://github.com/SSARCandy/ini-cpp
|
||||
*/
|
||||
|
||||
#ifndef __INI_H__
|
||||
#define __INI_H__
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace inih {
|
||||
|
||||
/* Typedef for prototype of handler function. */
|
||||
typedef int (*ini_handler)(void* user, const char* section, const char* name,
|
||||
const char* value);
|
||||
|
||||
/* Typedef for prototype of fgets-style reader function. */
|
||||
typedef char* (*ini_reader)(char* str, int num, void* stream);
|
||||
|
||||
#define INI_STOP_ON_FIRST_ERROR 1
|
||||
#define INI_MAX_LINE 2000
|
||||
#define INI_INITIAL_ALLOC 200
|
||||
#define MAX_SECTION 50
|
||||
#define MAX_NAME 50
|
||||
#define INI_START_COMMENT_PREFIXES ";#"
|
||||
#define INI_INLINE_COMMENT_PREFIXES ";"
|
||||
|
||||
/* Strip whitespace chars off end of given string, in place. Return s. */
|
||||
inline static char* rstrip(char* s) {
|
||||
char* p = s + strlen(s);
|
||||
while (p > s && isspace((unsigned char)(*--p))) *p = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Return pointer to first non-whitespace char in given string. */
|
||||
inline static char* lskip(const char* s) {
|
||||
while (*s && isspace((unsigned char)(*s))) s++;
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Return pointer to first char (of chars) or inline comment in given string,
|
||||
or pointer to null at end of string if neither found. Inline comment must
|
||||
be prefixed by a whitespace character to register as a comment. */
|
||||
inline static char* find_chars_or_comment(const char* s, const char* chars) {
|
||||
int was_space = 0;
|
||||
while (*s && (!chars || !strchr(chars, *s)) &&
|
||||
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
|
||||
was_space = isspace((unsigned char)(*s));
|
||||
s++;
|
||||
}
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
|
||||
inline static char* strncpy0(char* dest, const char* src, size_t size) {
|
||||
strncpy(dest, src, size - 1);
|
||||
dest[size - 1] = '\0';
|
||||
return dest;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
inline int ini_parse_stream(ini_reader reader, void* stream,
|
||||
ini_handler handler, void* user) {
|
||||
/* Uses a fair bit of stack (use heap instead if you need to) */
|
||||
char* line;
|
||||
size_t max_line = INI_INITIAL_ALLOC;
|
||||
char* new_line;
|
||||
size_t offset;
|
||||
char section[MAX_SECTION] = "";
|
||||
char prev_name[MAX_NAME] = "";
|
||||
|
||||
char* start;
|
||||
char* end;
|
||||
char* name;
|
||||
char* value;
|
||||
int lineno = 0;
|
||||
int error = 0;
|
||||
|
||||
line = (char*)malloc(INI_INITIAL_ALLOC);
|
||||
if (!line) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
#if INI_HANDLER_LINENO
|
||||
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
|
||||
#else
|
||||
#define HANDLER(u, s, n, v) handler(u, s, n, v)
|
||||
#endif
|
||||
|
||||
/* Scan through stream line by line */
|
||||
while (reader(line, (int)max_line, stream) != NULL) {
|
||||
offset = strlen(line);
|
||||
while (offset == max_line - 1 && line[offset - 1] != '\n') {
|
||||
max_line *= 2;
|
||||
if (max_line > INI_MAX_LINE) max_line = INI_MAX_LINE;
|
||||
new_line = (char*)realloc(line, max_line);
|
||||
if (!new_line) {
|
||||
free(line);
|
||||
return -2;
|
||||
}
|
||||
line = new_line;
|
||||
if (reader(line + offset, (int)(max_line - offset), stream) == NULL)
|
||||
break;
|
||||
if (max_line >= INI_MAX_LINE) break;
|
||||
offset += strlen(line + offset);
|
||||
}
|
||||
|
||||
lineno++;
|
||||
|
||||
start = line;
|
||||
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
|
||||
(unsigned char)start[1] == 0xBB &&
|
||||
(unsigned char)start[2] == 0xBF) {
|
||||
start += 3;
|
||||
}
|
||||
start = lskip(rstrip(start));
|
||||
|
||||
if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
|
||||
/* Start-of-line comment */
|
||||
} else if (*start == '[') {
|
||||
/* A "[section]" line */
|
||||
end = find_chars_or_comment(start + 1, "]");
|
||||
if (*end == ']') {
|
||||
*end = '\0';
|
||||
strncpy0(section, start + 1, sizeof(section));
|
||||
*prev_name = '\0';
|
||||
} else if (!error) {
|
||||
/* No ']' found on section line */
|
||||
error = lineno;
|
||||
}
|
||||
} else if (*start) {
|
||||
/* Not a comment, must be a name[=:]value pair */
|
||||
end = find_chars_or_comment(start, "=:");
|
||||
if (*end == '=' || *end == ':') {
|
||||
*end = '\0';
|
||||
name = rstrip(start);
|
||||
value = end + 1;
|
||||
end = find_chars_or_comment(value, NULL);
|
||||
if (*end) *end = '\0';
|
||||
value = lskip(value);
|
||||
rstrip(value);
|
||||
|
||||
/* Valid name[=:]value pair found, call handler */
|
||||
strncpy0(prev_name, name, sizeof(prev_name));
|
||||
if (!HANDLER(user, section, name, value) && !error)
|
||||
error = lineno;
|
||||
} else if (!error) {
|
||||
/* No '=' or ':' found on name[=:]value line */
|
||||
error = lineno;
|
||||
}
|
||||
}
|
||||
|
||||
if (error) break;
|
||||
}
|
||||
|
||||
free(line);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
inline int ini_parse_file(FILE* file, ini_handler handler, void* user) {
|
||||
return ini_parse_stream((ini_reader)fgets, file, handler, user);
|
||||
}
|
||||
|
||||
inline int ini_parse(const char* filename, ini_handler handler, void* user) {
|
||||
FILE* file;
|
||||
int error;
|
||||
|
||||
file = fopen(filename, "r");
|
||||
if (!file) return -1;
|
||||
error = ini_parse_file(file, handler, user);
|
||||
fclose(file);
|
||||
return error;
|
||||
}
|
||||
|
||||
#endif /* __INI_H__ */
|
||||
|
||||
#ifndef __INIREADER_H__
|
||||
#define __INIREADER_H__
|
||||
|
||||
// Read an INI file into easy-to-access name/value pairs. (Note that I've gone
|
||||
// for simplicity here rather than speed, but it should be pretty decent.)
|
||||
class INIReader {
|
||||
public:
|
||||
// Empty Constructor
|
||||
INIReader(){};
|
||||
|
||||
// Construct INIReader and parse given filename. See ini.h for more info
|
||||
// about the parsing.
|
||||
INIReader(std::string filename);
|
||||
|
||||
// Construct INIReader and parse given file. See ini.h for more info
|
||||
// about the parsing.
|
||||
INIReader(FILE* file);
|
||||
|
||||
// Return the result of ini_parse(), i.e., 0 on success, line number of
|
||||
// first error on parse error, or -1 on file open error.
|
||||
int ParseError() const;
|
||||
|
||||
// Return the list of sections found in ini file
|
||||
const std::set<std::string> Sections() const;
|
||||
|
||||
// Return the list of keys in the given section
|
||||
const std::set<std::string> Keys(std::string section) const;
|
||||
|
||||
const std::unordered_map<std::string, std::string> Get(
|
||||
std::string section) const;
|
||||
|
||||
template <typename T = std::string>
|
||||
T Get(const std::string& section, const std::string& name) const;
|
||||
|
||||
template <typename T>
|
||||
T Get(const std::string& section, const std::string& name,
|
||||
T&& default_v) const;
|
||||
|
||||
template <typename T = std::string>
|
||||
std::vector<T> GetVector(const std::string& section,
|
||||
const std::string& name) const;
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> GetVector(const std::string& section,
|
||||
const std::string& name,
|
||||
const std::vector<T>& default_v) const;
|
||||
|
||||
template <typename T = std::string>
|
||||
void InsertEntry(const std::string& section, const std::string& name,
|
||||
const T& v);
|
||||
|
||||
template <typename T = std::string>
|
||||
void InsertEntry(const std::string& section, const std::string& name,
|
||||
const std::vector<T>& vs);
|
||||
|
||||
template <typename T = std::string>
|
||||
void UpdateEntry(const std::string& section, const std::string& name,
|
||||
const T& v);
|
||||
|
||||
template <typename T = std::string>
|
||||
void UpdateEntry(const std::string& section, const std::string& name,
|
||||
const std::vector<T>& vs);
|
||||
|
||||
protected:
|
||||
int _error;
|
||||
std::unordered_map<std::string,
|
||||
std::unordered_map<std::string, std::string>>
|
||||
_values;
|
||||
static int ValueHandler(void* user, const char* section, const char* name,
|
||||
const char* value);
|
||||
|
||||
template <typename T>
|
||||
T Converter(const std::string& s) const;
|
||||
|
||||
const bool BoolConverter(std::string s) const;
|
||||
|
||||
template <typename T>
|
||||
std::string V2String(const T& v) const;
|
||||
|
||||
template <typename T>
|
||||
std::string Vec2String(const std::vector<T>& v) const;
|
||||
};
|
||||
|
||||
#endif // __INIREADER_H__
|
||||
|
||||
#ifndef __INIREADER__
|
||||
#define __INIREADER__
|
||||
|
||||
/**
|
||||
* @brief Construct an INIReader object from a file name
|
||||
* @param filename The name of the INI file to parse
|
||||
* @throws std::runtime_error if there is an error parsing the INI file
|
||||
*/
|
||||
inline INIReader::INIReader(std::string filename) {
|
||||
_error = ini_parse(filename.c_str(), ValueHandler, this);
|
||||
ParseError();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct an INIReader object from a file pointer
|
||||
* @param file A pointer to the INI file to parse
|
||||
* @throws std::runtime_error if there is an error parsing the INI file
|
||||
*/
|
||||
inline INIReader::INIReader(FILE* file) {
|
||||
_error = ini_parse_file(file, ValueHandler, this);
|
||||
ParseError();
|
||||
}
|
||||
|
||||
inline int INIReader::ParseError() const {
|
||||
switch (_error) {
|
||||
case 0:
|
||||
break;
|
||||
case -1:
|
||||
throw std::runtime_error("ini file not found.");
|
||||
case -2:
|
||||
throw std::runtime_error("memory alloc error");
|
||||
default:
|
||||
throw std::runtime_error("parse error on line no: " +
|
||||
std::to_string(_error));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the list of sections found in ini file
|
||||
* @return The list of sections found in ini file
|
||||
*/
|
||||
inline const std::set<std::string> INIReader::Sections() const {
|
||||
std::set<std::string> retval;
|
||||
for (auto const& element : _values) {
|
||||
retval.insert(element.first);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the list of keys in the given section
|
||||
* @param section The section name
|
||||
* @return The list of keys in the given section
|
||||
*/
|
||||
inline const std::set<std::string> INIReader::Keys(std::string section) const {
|
||||
auto const _section = Get(section);
|
||||
std::set<std::string> retval;
|
||||
for (auto const& element : _section) {
|
||||
retval.insert(element.first);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the map representing the values in a section of the INI file
|
||||
* @param section The name of the section to retrieve
|
||||
* @return The map representing the values in the given section
|
||||
* @throws std::runtime_error if the section is not found
|
||||
*/
|
||||
inline const std::unordered_map<std::string, std::string> INIReader::Get(
|
||||
std::string section) const {
|
||||
auto const _section = _values.find(section);
|
||||
if (_section == _values.end()) {
|
||||
throw std::runtime_error("section '" + section + "' not found.");
|
||||
}
|
||||
return _section->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the value of the given key in the given section
|
||||
* @param section The section name
|
||||
* @param name The key name
|
||||
* @return The value of the given key in the given section
|
||||
*/
|
||||
template <typename T>
|
||||
inline T INIReader::Get(const std::string& section,
|
||||
const std::string& name) const {
|
||||
auto const _section = Get(section);
|
||||
auto const _value = _section.find(name);
|
||||
|
||||
if (_value == _section.end()) {
|
||||
throw std::runtime_error("key '" + name + "' not found in section '" +
|
||||
section + "'.");
|
||||
}
|
||||
|
||||
std::string value = _value->second;
|
||||
|
||||
if constexpr (std::is_same<T, std::string>()) {
|
||||
return value;
|
||||
} else if constexpr (std::is_same<T, bool>()) {
|
||||
return BoolConverter(value);
|
||||
} else {
|
||||
return Converter<T>(value);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the value of the given key in the given section, return default
|
||||
* if not found
|
||||
* @param section The section name
|
||||
* @param name The key name
|
||||
* @param default_v The default value
|
||||
* @return The value of the given key in the given section, return default if
|
||||
* not found
|
||||
*/
|
||||
template <typename T>
|
||||
inline T INIReader::Get(const std::string& section, const std::string& name,
|
||||
T&& default_v) const {
|
||||
try {
|
||||
return Get<T>(section, name);
|
||||
} catch (std::runtime_error&) {
|
||||
return default_v;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the value array of the given key in the given section.
|
||||
* @param section The section name
|
||||
* @param name The key name
|
||||
* @return The value array of the given key in the given section.
|
||||
*
|
||||
* For example:
|
||||
* ```ini
|
||||
* [section]
|
||||
* key = 1 2 3 4
|
||||
* ```
|
||||
* ```cpp
|
||||
* const auto vs = ini.GetVector<std::vector<int>>("section", "key");
|
||||
* // vs = {1, 2, 3, 4}
|
||||
* ```
|
||||
*/
|
||||
template <typename T>
|
||||
inline std::vector<T> INIReader::GetVector(const std::string& section,
|
||||
const std::string& name) const {
|
||||
std::string value = Get(section, name);
|
||||
|
||||
std::istringstream out{value};
|
||||
const std::vector<std::string> strs{std::istream_iterator<std::string>{out},
|
||||
std::istream_iterator<std::string>()};
|
||||
try {
|
||||
std::vector<T> vs{};
|
||||
for (const std::string& s : strs) {
|
||||
vs.emplace_back(Converter<T>(s));
|
||||
}
|
||||
return vs;
|
||||
} catch (std::exception&) {
|
||||
throw std::runtime_error("cannot parse value " + value +
|
||||
" to vector<T>.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the value array of the given key in the given section, return
|
||||
* default if not found
|
||||
* @param section The section name
|
||||
* @param name The key name
|
||||
* @param default_v The default value
|
||||
* @return The value array of the given key in the given section, return default
|
||||
* if not found
|
||||
*
|
||||
* @see INIReader::GetVector
|
||||
*/
|
||||
template <typename T>
|
||||
inline std::vector<T> INIReader::GetVector(
|
||||
const std::string& section, const std::string& name,
|
||||
const std::vector<T>& default_v) const {
|
||||
try {
|
||||
return GetVector<T>(section, name);
|
||||
} catch (std::runtime_error&) {
|
||||
return default_v;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Insert a key-value pair into the INI file
|
||||
* @param section The section name
|
||||
* @param name The key name
|
||||
* @param v The value to insert
|
||||
* @throws std::runtime_error if the key already exists in the section
|
||||
*/
|
||||
template <typename T>
|
||||
inline void INIReader::InsertEntry(const std::string& section,
|
||||
const std::string& name, const T& v) {
|
||||
if (_values[section][name].size() > 0) {
|
||||
throw std::runtime_error("duplicate key '" + std::string(name) +
|
||||
"' in section '" + section + "'.");
|
||||
}
|
||||
_values[section][name] = V2String(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Insert a vector of values into the INI file
|
||||
* @param section The section name
|
||||
* @param name The key name
|
||||
* @param vs The vector of values to insert
|
||||
* @throws std::runtime_error if the key already exists in the section
|
||||
*/
|
||||
template <typename T>
|
||||
inline void INIReader::InsertEntry(const std::string& section,
|
||||
const std::string& name,
|
||||
const std::vector<T>& vs) {
|
||||
if (_values[section][name].size() > 0) {
|
||||
throw std::runtime_error("duplicate key '" + std::string(name) +
|
||||
"' in section '" + section + "'.");
|
||||
}
|
||||
_values[section][name] = Vec2String(vs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update a key-value pair in the INI file
|
||||
* @param section The section name
|
||||
* @param name The key name
|
||||
* @param v The new value to set
|
||||
* @throws std::runtime_error if the key does not exist in the section
|
||||
*/
|
||||
template <typename T>
|
||||
inline void INIReader::UpdateEntry(const std::string& section,
|
||||
const std::string& name, const T& v) {
|
||||
if (!_values[section][name].size()) {
|
||||
throw std::runtime_error("key '" + std::string(name) +
|
||||
"' not exist in section '" + section + "'.");
|
||||
}
|
||||
_values[section][name] = V2String(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update a vector of values in the INI file
|
||||
* @param section The section name
|
||||
* @param name The key name
|
||||
* @param vs The new vector of values to set
|
||||
* @throws std::runtime_error if the key does not exist in the section
|
||||
*/
|
||||
template <typename T>
|
||||
inline void INIReader::UpdateEntry(const std::string& section,
|
||||
const std::string& name,
|
||||
const std::vector<T>& vs) {
|
||||
if (!_values[section][name].size()) {
|
||||
throw std::runtime_error("key '" + std::string(name) +
|
||||
"' not exist in section '" + section + "'.");
|
||||
}
|
||||
_values[section][name] = Vec2String(vs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::string INIReader::V2String(const T& v) const {
|
||||
std::stringstream ss;
|
||||
ss << v;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::string INIReader::Vec2String(const std::vector<T>& v) const {
|
||||
if (v.empty()) {
|
||||
return "";
|
||||
}
|
||||
std::ostringstream oss;
|
||||
std::copy(v.begin(), v.end() - 1, std::ostream_iterator<T>(oss, " "));
|
||||
oss << v.back();
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T INIReader::Converter(const std::string& s) const {
|
||||
try {
|
||||
T v{};
|
||||
std::istringstream _{s};
|
||||
_.exceptions(std::ios::failbit);
|
||||
_ >> v;
|
||||
return v;
|
||||
} catch (std::exception&) {
|
||||
throw std::runtime_error("cannot parse value '" + s + "' to type<T>.");
|
||||
};
|
||||
}
|
||||
|
||||
inline const bool INIReader::BoolConverter(std::string s) const {
|
||||
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
|
||||
static const std::unordered_map<std::string, bool> s2b{
|
||||
{"1", true}, {"true", true}, {"yes", true}, {"on", true},
|
||||
{"0", false}, {"false", false}, {"no", false}, {"off", false},
|
||||
};
|
||||
auto const value = s2b.find(s);
|
||||
if (value == s2b.end()) {
|
||||
throw std::runtime_error("'" + s + "' is not a valid boolean value.");
|
||||
}
|
||||
return value->second;
|
||||
}
|
||||
|
||||
inline int INIReader::ValueHandler(void* user, const char* section,
|
||||
const char* name, const char* value) {
|
||||
INIReader* reader = (INIReader*)user;
|
||||
if (reader->_values[section][name].size() > 0) {
|
||||
throw std::runtime_error("duplicate key '" + std::string(name) +
|
||||
"' in section '" + section + "'.");
|
||||
}
|
||||
reader->_values[section][name] = value;
|
||||
return 1;
|
||||
}
|
||||
#endif // __INIREADER__
|
||||
|
||||
#ifndef __INIWRITER_H__
|
||||
#define __INIWRITER_H__
|
||||
|
||||
class INIWriter {
|
||||
public:
|
||||
INIWriter(){};
|
||||
/**
|
||||
* @brief Write the contents of an INI file to a new file
|
||||
* @param filepath The path of the output file
|
||||
* @param reader The INIReader object to write to the file
|
||||
* @throws std::runtime_error if the output file already exists or cannot be
|
||||
* opened
|
||||
*/
|
||||
inline static void write(const std::string& filepath,
|
||||
const INIReader& reader) {
|
||||
if (struct stat buf; stat(filepath.c_str(), &buf) == 0) {
|
||||
throw std::runtime_error("file: " + filepath + " already exist.");
|
||||
}
|
||||
std::ofstream out;
|
||||
out.open(filepath);
|
||||
if (!out.is_open()) {
|
||||
throw std::runtime_error("cannot open output file: " + filepath);
|
||||
}
|
||||
for (const auto& section : reader.Sections()) {
|
||||
out << "[" << section << "]\n";
|
||||
for (const auto& key : reader.Keys(section)) {
|
||||
out << key << "=" << reader.Get(section, key) << "\n";
|
||||
}
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif /* __INIWRITER_H__ */
|
74
src/main.cpp
Normal file
74
src/main.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
#include <QApplication>
|
||||
#include <QLabel>
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "dock.h"
|
||||
|
||||
void customMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
|
||||
// static QFile logFile("debug_output.log");
|
||||
// if (!logFile.isOpen()) {
|
||||
// logFile.open(QIODevice::Append | QIODevice::Text);
|
||||
// }
|
||||
|
||||
// QTextStream out(&logFile);
|
||||
QTextStream console(stdout);
|
||||
|
||||
QString levelStr;
|
||||
QString colorStart;
|
||||
const QString colorEnd = "\033[0m";
|
||||
|
||||
switch (type) {
|
||||
case QtDebugMsg:
|
||||
levelStr = "DEBUG";
|
||||
colorStart = "\033[36m"; // Cyan
|
||||
break;
|
||||
case QtInfoMsg:
|
||||
levelStr = "INFO";
|
||||
colorStart = "\033[32m"; // Green
|
||||
break;
|
||||
case QtWarningMsg:
|
||||
levelStr = "WARNING";
|
||||
colorStart = "\033[33m"; // Yellow
|
||||
break;
|
||||
case QtCriticalMsg:
|
||||
levelStr = "CRITICAL";
|
||||
colorStart = "\033[31m"; // Red
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
levelStr = "FATAL";
|
||||
colorStart = "\033[41m\033[97m"; // Red background, white text
|
||||
break;
|
||||
}
|
||||
|
||||
QString time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
|
||||
|
||||
// Plain version for log file
|
||||
QString plainText = QString("[%1] [%2] %3 (%4:%5)")
|
||||
.arg(time, levelStr, msg, context.file, QString::number(context.line));
|
||||
|
||||
// Colored level only for console
|
||||
QString coloredText = QString("[%1] [%2%3%4] %5 (%6:%7)")
|
||||
.arg(time, colorStart, levelStr, colorEnd, msg, context.file, QString::number(context.line));
|
||||
|
||||
// out << plainText << "\n";
|
||||
// out.flush();
|
||||
|
||||
console << coloredText << "\n";
|
||||
console.flush();
|
||||
|
||||
if (type == QtFatalMsg)
|
||||
abort();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
qInstallMessageHandler(customMessageHandler);
|
||||
|
||||
QApplication app(argc, argv);
|
||||
Dock dock{&app};
|
||||
|
||||
return 0;
|
||||
}
|
217
src/xdg.h
Normal file
217
src/xdg.h
Normal file
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* Copyright © 2020 Danilo Spinella <oss@danyspin97.org>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
namespace xdg {
|
||||
|
||||
class BaseDirectoryException : public std::exception {
|
||||
public:
|
||||
explicit BaseDirectoryException(std::string msg) : msg_(std::move(msg)) {}
|
||||
|
||||
[[nodiscard]] auto what() const noexcept -> const char * override {
|
||||
return msg_.c_str();
|
||||
}
|
||||
[[nodiscard]] auto msg() const noexcept -> std::string { return msg_; }
|
||||
|
||||
private:
|
||||
const std::string msg_;
|
||||
};
|
||||
|
||||
class BaseDirectories {
|
||||
public:
|
||||
BaseDirectories() {
|
||||
const char *home_env = getenv("HOME");
|
||||
if (home_env == nullptr) {
|
||||
throw BaseDirectoryException("$HOME must be set");
|
||||
}
|
||||
home_ = std::filesystem::path{home_env};
|
||||
|
||||
data_home_ = GetAbsolutePathFromEnvOrDefault(
|
||||
"XDG_DATA_HOME", home_ / ".local" / "share");
|
||||
config_home_ = GetAbsolutePathFromEnvOrDefault("XDG_CONFIG_HOME",
|
||||
home_ / ".config");
|
||||
data_ = GetPathsFromEnvOrDefault("XDG_DATA_DIRS",
|
||||
std::vector<std::filesystem::path>{
|
||||
"/usr/local/share", "/usr/share"});
|
||||
config_ = GetPathsFromEnvOrDefault(
|
||||
"XDG_CONFIG_DIRS", std::vector<std::filesystem::path>{"/etc/xdg"});
|
||||
cache_home_ =
|
||||
GetAbsolutePathFromEnvOrDefault("XDG_CACHE_HOME", home_ / ".cache");
|
||||
|
||||
SetRuntimeDir();
|
||||
}
|
||||
|
||||
static auto GetInstance() -> BaseDirectories & {
|
||||
static BaseDirectories dirs;
|
||||
|
||||
return dirs;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto DataHome() -> const std::filesystem::path & {
|
||||
return data_home_;
|
||||
}
|
||||
[[nodiscard]] auto ConfigHome() -> const std::filesystem::path & {
|
||||
return config_home_;
|
||||
}
|
||||
[[nodiscard]] auto Data() -> const std::vector<std::filesystem::path> & {
|
||||
return data_;
|
||||
}
|
||||
[[nodiscard]] auto Config() -> const std::vector<std::filesystem::path> & {
|
||||
return config_;
|
||||
}
|
||||
[[nodiscard]] auto CacheHome() -> const std::filesystem::path & {
|
||||
return cache_home_;
|
||||
}
|
||||
[[nodiscard]] auto Runtime()
|
||||
-> const std::optional<std::filesystem::path> & {
|
||||
return runtime_;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetRuntimeDir() {
|
||||
const char *runtime_env = getenv("XDG_RUNTIME_DIR");
|
||||
if (runtime_env != nullptr) {
|
||||
std::filesystem::path runtime_dir{runtime_env};
|
||||
if (runtime_dir.is_absolute()) {
|
||||
if (!std::filesystem::exists(runtime_dir)) {
|
||||
throw BaseDirectoryException(
|
||||
"$XDG_RUNTIME_DIR must exist on the system");
|
||||
}
|
||||
|
||||
auto runtime_dir_perms =
|
||||
std::filesystem::status(runtime_dir).permissions();
|
||||
using perms = std::filesystem::perms;
|
||||
// Check XDG_RUNTIME_DIR permissions are 0700
|
||||
if (((runtime_dir_perms & perms::owner_all) == perms::none) ||
|
||||
((runtime_dir_perms & perms::group_all) != perms::none) ||
|
||||
((runtime_dir_perms & perms::others_all) != perms::none)) {
|
||||
throw BaseDirectoryException(
|
||||
"$XDG_RUNTIME_DIR must have 0700 as permissions");
|
||||
}
|
||||
runtime_.emplace(runtime_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static auto
|
||||
GetAbsolutePathFromEnvOrDefault(const char *env_name,
|
||||
std::filesystem::path &&default_path)
|
||||
-> std::filesystem::path {
|
||||
const char *env_var = getenv(env_name);
|
||||
if (env_var == nullptr) {
|
||||
return std::move(default_path);
|
||||
}
|
||||
auto path = std::filesystem::path{env_var};
|
||||
if (!path.is_absolute()) {
|
||||
return std::move(default_path);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static auto
|
||||
GetPathsFromEnvOrDefault(const char *env_name,
|
||||
std::vector<std::filesystem::path> &&default_paths)
|
||||
-> std::vector<std::filesystem::path> {
|
||||
auto *env = getenv(env_name);
|
||||
if (env == nullptr) {
|
||||
return std::move(default_paths);
|
||||
}
|
||||
std::string paths{env};
|
||||
|
||||
std::vector<std::filesystem::path> dirs{};
|
||||
size_t start = 0;
|
||||
size_t pos = 0;
|
||||
while ((pos = paths.find_first_of(':', start)) != std::string::npos) {
|
||||
std::filesystem::path current_path{
|
||||
paths.substr(start, pos - start)};
|
||||
if (current_path.is_absolute() &&
|
||||
!VectorContainsPath(dirs, current_path)) {
|
||||
dirs.emplace_back(current_path);
|
||||
}
|
||||
start = pos + 1;
|
||||
}
|
||||
std::filesystem::path current_path{paths.substr(start, pos - start)};
|
||||
if (current_path.is_absolute() &&
|
||||
!VectorContainsPath(dirs, current_path)) {
|
||||
dirs.emplace_back(current_path);
|
||||
}
|
||||
|
||||
if (dirs.empty()) {
|
||||
return std::move(default_paths);
|
||||
}
|
||||
|
||||
return dirs;
|
||||
}
|
||||
|
||||
static auto
|
||||
VectorContainsPath(const std::vector<std::filesystem::path> &paths,
|
||||
const std::filesystem::path &path) -> bool {
|
||||
return std::find(std::begin(paths), std::end(paths), path) !=
|
||||
paths.end();
|
||||
}
|
||||
|
||||
std::filesystem::path home_;
|
||||
|
||||
std::filesystem::path data_home_;
|
||||
std::filesystem::path config_home_;
|
||||
std::vector<std::filesystem::path> data_;
|
||||
std::vector<std::filesystem::path> config_;
|
||||
std::filesystem::path cache_home_;
|
||||
std::optional<std::filesystem::path> runtime_;
|
||||
|
||||
}; // namespace xdg
|
||||
|
||||
[[nodiscard]] inline auto DataHomeDir() -> const std::filesystem::path & {
|
||||
return BaseDirectories::GetInstance().DataHome();
|
||||
}
|
||||
[[nodiscard]] inline auto ConfigHomeDir() -> const std::filesystem::path & {
|
||||
return BaseDirectories::GetInstance().ConfigHome();
|
||||
}
|
||||
[[nodiscard]] inline auto DataDirs()
|
||||
-> const std::vector<std::filesystem::path> & {
|
||||
return BaseDirectories::GetInstance().Data();
|
||||
}
|
||||
[[nodiscard]] inline auto ConfigDirs()
|
||||
-> const std::vector<std::filesystem::path> & {
|
||||
return BaseDirectories::GetInstance().Config();
|
||||
}
|
||||
[[nodiscard]] inline auto CacheHomeDir() -> const std::filesystem::path & {
|
||||
return BaseDirectories::GetInstance().CacheHome();
|
||||
}
|
||||
[[nodiscard]] inline auto RuntimeDir()
|
||||
-> const std::optional<std::filesystem::path> & {
|
||||
return BaseDirectories::GetInstance().Runtime();
|
||||
}
|
||||
|
||||
} // namespace xdg
|
||||
|
12
waydock.pro
Normal file
12
waydock.pro
Normal file
|
@ -0,0 +1,12 @@
|
|||
QT += core gui widgets
|
||||
|
||||
CONFIG += c++17 debug console
|
||||
TEMPLATE = app
|
||||
TARGET = waydock
|
||||
|
||||
SOURCES += \
|
||||
src/main.cpp \
|
||||
src/dock.cpp \
|
||||
|
||||
# HEADERS += \
|
||||
# src/some_header.h
|
Loading…
Add table
Add a link
Reference in a new issue