Compare commits
10 commits
fcc080871a
...
0bc2decb7c
Author | SHA1 | Date | |
---|---|---|---|
0bc2decb7c |
|||
edc7552b5c |
|||
e72da82b32 |
|||
d1116e7721 |
|||
3cbf6d5645 |
|||
b47928cbab |
|||
87d445340a |
|||
b4fcdab7f4 |
|||
adc2bb2baf |
|||
48fa1cc852 |
16 changed files with 1154 additions and 114 deletions
33
.editorconfig
Normal file
33
.editorconfig
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.c]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
|
||||||
|
[*.h]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
tab_width = 4
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.mk]
|
||||||
|
indent_style = tab
|
||||||
|
tab_width = 4
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.sh]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
|
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
8
.gitattributes
vendored
Normal file
8
.gitattributes
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Vendored headers are vendored code, to the surprise of absolutely noone.
|
||||||
|
# See:
|
||||||
|
# <https://github.com/github-linguist/linguist/blob/main/docs/overrides.md#vendored-code>
|
||||||
|
/include/chroma.h linguist-vendored
|
||||||
|
|
||||||
|
# Don't think linguist can detect XML, but let's tell it that the protocols are
|
||||||
|
# vendored *anyway*.
|
||||||
|
/protocols/*.xml linguist-vendored
|
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/bin
|
||||||
|
/obj
|
||||||
|
|
||||||
|
# Generated by wayland-scanner
|
||||||
|
include/xdg-shell.h
|
||||||
|
include/wlr-layer-shell-unstable-v1.h
|
||||||
|
src/xdg-shell.c
|
||||||
|
src/wlr-layer-shell-unstable-v1.c
|
328
LICENSE
Normal file
328
LICENSE
Normal file
|
@ -0,0 +1,328 @@
|
||||||
|
Mozilla Public License, version 2.0
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
|
||||||
|
1.1. “Contributor”
|
||||||
|
means each individual or legal entity that creates, contributes to the
|
||||||
|
creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. “Contributor Version”
|
||||||
|
means the combination of the Contributions of others (if any) used by a
|
||||||
|
Contributor and that particular Contributor’s Contribution.
|
||||||
|
|
||||||
|
1.3. “Contribution”
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. “Covered Software”
|
||||||
|
means Source Code Form to which the initial Contributor has attached the
|
||||||
|
notice in Exhibit A, the Executable Form of such Source Code Form,
|
||||||
|
and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
|
1.5. “Incompatible With Secondary Licenses”
|
||||||
|
means
|
||||||
|
|
||||||
|
a. that the initial Contributor has attached the notice described
|
||||||
|
in Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
b. that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the terms
|
||||||
|
of a Secondary License.
|
||||||
|
|
||||||
|
1.6. “Executable Form”
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. “Larger Work”
|
||||||
|
means a work that combines Covered Software with other material,
|
||||||
|
in a separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. “License”
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. “Licensable”
|
||||||
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently,
|
||||||
|
any and all of the rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. “Modifications”
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
a. any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered Software; or
|
||||||
|
|
||||||
|
b. any new file in Source Code Form that contains any Covered Software.
|
||||||
|
|
||||||
|
1.11. “Patent Claims” of a Contributor
|
||||||
|
means any patent claim(s), including without limitation, method, process,
|
||||||
|
and apparatus claims, in any patent Licensable by such Contributor that
|
||||||
|
would be infringed, but for the grant of the License, by the making,
|
||||||
|
using, selling, offering for sale, having made, import, or transfer of
|
||||||
|
either its Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
1.12. “Secondary License”
|
||||||
|
means either the GNU General Public License, Version 2.0, the
|
||||||
|
GNU Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those licenses.
|
||||||
|
|
||||||
|
1.13. “Source Code Form”
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. “You” (or “Your”)
|
||||||
|
means an individual or a legal entity exercising rights under this License.
|
||||||
|
For legal entities, “You” includes any entity that controls,
|
||||||
|
is controlled by, or is under common control with You. For purposes of
|
||||||
|
this definition, “control” means (a) the power, direct or indirect,
|
||||||
|
to cause the direction or management of such entity, whether by contract
|
||||||
|
or otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||||
|
outstanding shares or beneficial ownership of such entity.
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
a. under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications,
|
||||||
|
or as part of a Larger Work; and
|
||||||
|
|
||||||
|
b. under Patent Claims of such Contributor to make, use, sell,
|
||||||
|
offer for sale, have made, import, and otherwise transfer either
|
||||||
|
its Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor
|
||||||
|
first distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
The licenses granted in this Section 2 are the only rights granted
|
||||||
|
under this License. No additional rights or licenses will be implied
|
||||||
|
from the distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted
|
||||||
|
by a Contributor:
|
||||||
|
|
||||||
|
a. for any code that a Contributor has removed from
|
||||||
|
Covered Software; or
|
||||||
|
|
||||||
|
b. for infringements caused by: (i) Your and any other third party’s
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its
|
||||||
|
Contributor Version); or
|
||||||
|
|
||||||
|
c. under Patent Claims infringed by Covered Software in the
|
||||||
|
absence of its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License
|
||||||
|
(if permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing,
|
||||||
|
or other equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the
|
||||||
|
licenses granted in Section 2.1.
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
All distribution of Covered Software in Source Code Form, including
|
||||||
|
any Modifications that You create or to which You contribute, must be
|
||||||
|
under the terms of this License. You must inform recipients that the
|
||||||
|
Source Code Form of the Covered Software is governed by the terms
|
||||||
|
of this License, and how they can obtain a copy of this License.
|
||||||
|
You may not attempt to alter or restrict the recipients’ rights
|
||||||
|
in the Source Code Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
a. such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more than
|
||||||
|
the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
b. You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients’ rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of
|
||||||
|
Covered Software with a work governed by one or more Secondary Licenses,
|
||||||
|
and the Covered Software is not Incompatible With Secondary Licenses,
|
||||||
|
this License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the
|
||||||
|
Covered Software under the terms of either this License or such
|
||||||
|
Secondary License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of
|
||||||
|
Covered Software. However, You may do so only on Your own behalf,
|
||||||
|
and not on behalf of any Contributor. You must make it absolutely clear
|
||||||
|
that any such warranty, support, indemnity, or liability obligation is
|
||||||
|
offered by You alone, and You hereby agree to indemnify every Contributor
|
||||||
|
for any liability incurred by such Contributor as a result of warranty,
|
||||||
|
support, indemnity or liability terms You offer. You may include
|
||||||
|
additional disclaimers of warranty and limitations of liability
|
||||||
|
specific to any jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this License
|
||||||
|
with respect to some or all of the Covered Software due to statute,
|
||||||
|
judicial order, or regulation then You must: (a) comply with the terms of
|
||||||
|
this License to the maximum extent possible; and (b) describe the limitations
|
||||||
|
and the code they affect. Such description must be placed in a text file
|
||||||
|
included with all distributions of the Covered Software under this License.
|
||||||
|
Except to the extent prohibited by statute or regulation, such description
|
||||||
|
must be sufficiently detailed for a recipient of ordinary skill
|
||||||
|
to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated (a) provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means,
|
||||||
|
this is the first time You have received notice of non-compliance with
|
||||||
|
this License from such Contributor, and You become compliant prior to
|
||||||
|
30 days after Your receipt of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted
|
||||||
|
to You by any and all Contributors for the Covered Software under
|
||||||
|
Section 2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
6. Disclaimer of Warranty
|
||||||
|
|
||||||
|
Covered Software is provided under this License on an “as is” basis, without
|
||||||
|
warranty of any kind, either expressed, implied, or statutory, including,
|
||||||
|
without limitation, warranties that the Covered Software is free of defects,
|
||||||
|
merchantable, fit for a particular purpose or non-infringing. The entire risk
|
||||||
|
as to the quality and performance of the Covered Software is with You.
|
||||||
|
Should any Covered Software prove defective in any respect, You
|
||||||
|
(not any Contributor) assume the cost of any necessary servicing, repair,
|
||||||
|
or correction. This disclaimer of warranty constitutes an essential part of
|
||||||
|
this License. No use of any Covered Software is authorized under this
|
||||||
|
License except under this disclaimer.
|
||||||
|
|
||||||
|
7. Limitation of Liability
|
||||||
|
|
||||||
|
Under no circumstances and under no legal theory, whether tort
|
||||||
|
(including negligence), contract, or otherwise, shall any Contributor, or
|
||||||
|
anyone who distributes Covered Software as permitted above, be liable to
|
||||||
|
You for any direct, indirect, special, incidental, or consequential damages
|
||||||
|
of any character including, without limitation, damages for lost profits,
|
||||||
|
loss of goodwill, work stoppage, computer failure or malfunction, or any and
|
||||||
|
all other commercial damages or losses, even if such party shall have been
|
||||||
|
informed of the possibility of such damages. This limitation of liability
|
||||||
|
shall not apply to liability for death or personal injury resulting from
|
||||||
|
such party’s negligence to the extent applicable law prohibits such
|
||||||
|
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||||
|
incidental or consequential damages, so this exclusion and limitation may
|
||||||
|
not apply to You.
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the courts of
|
||||||
|
a jurisdiction where the defendant maintains its principal place of business
|
||||||
|
and such litigation shall be governed by laws of that jurisdiction, without
|
||||||
|
reference to its conflict-of-law provisions. Nothing in this Section shall
|
||||||
|
prevent a party’s ability to bring cross-claims or counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject matter
|
||||||
|
hereof. If any provision of this License is held to be unenforceable,
|
||||||
|
such provision shall be reformed only to the extent necessary to make it
|
||||||
|
enforceable. Any law or regulation which provides that the language of a
|
||||||
|
contract shall be construed against the drafter shall not be used to construe
|
||||||
|
this License against a Contributor.
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in
|
||||||
|
Section 10.3, no one other than the license steward has the right to
|
||||||
|
modify or publish new versions of this License. Each version will be
|
||||||
|
given a distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published
|
||||||
|
by the license steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a modified
|
||||||
|
version of this License if you rename the license and remove any
|
||||||
|
references to the name of the license steward (except to note that such
|
||||||
|
modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is
|
||||||
|
Incompatible With Secondary Licenses
|
||||||
|
If You choose to distribute Source Code Form that is
|
||||||
|
Incompatible With Secondary Licenses under the terms of this version of
|
||||||
|
the License, the notice described in Exhibit B of this
|
||||||
|
License must be attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the
|
||||||
|
Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
|
||||||
|
with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular file,
|
||||||
|
then You may include the notice in a location (such as a LICENSE file in a
|
||||||
|
relevant directory) where a recipient would be likely to
|
||||||
|
look for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - “Incompatible With Secondary Licenses” Notice
|
||||||
|
|
||||||
|
This Source Code Form is “Incompatible With Secondary Licenses”,
|
||||||
|
as defined by the Mozilla Public License, v. 2.0.
|
173
Makefile
Normal file
173
Makefile
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
PROJECT_NAME = chroma
|
||||||
|
VERSION = 1.0.0
|
||||||
|
|
||||||
|
# Directories
|
||||||
|
SRCDIR = src
|
||||||
|
INCDIR = include
|
||||||
|
OBJDIR = obj
|
||||||
|
BINDIR = bin
|
||||||
|
SYSTEMD_DIR = systemd
|
||||||
|
PROTOCOLDIR = protocols
|
||||||
|
|
||||||
|
# Install directories
|
||||||
|
PREFIX ?= /usr/local
|
||||||
|
BINDIR_INSTALL = $(PREFIX)/bin
|
||||||
|
SYSTEMD_INSTALL = $(HOME)/.config/systemd/user
|
||||||
|
|
||||||
|
# Compiler and flags
|
||||||
|
CC = gcc
|
||||||
|
CFLAGS = -std=c11 -Wall -Wextra -Werror -pedantic -O2 -g
|
||||||
|
CFLAGS += -D_GNU_SOURCE -DCHROMA_VERSION=\"$(VERSION)\"
|
||||||
|
CPPFLAGS = -I$(INCDIR)
|
||||||
|
|
||||||
|
# Debug build flags
|
||||||
|
DEBUG_CFLAGS = -std=c11 -Wall -Wextra -Werror -pedantic -Og -g3 -DDEBUG
|
||||||
|
DEBUG_CFLAGS += -D_GNU_SOURCE -DCHROMA_VERSION=\"$(VERSION)-debug\"
|
||||||
|
DEBUG_CFLAGS += -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer
|
||||||
|
|
||||||
|
# Libraries using pkg-config
|
||||||
|
PKG_DEPS = wayland-client wayland-egl egl glesv2 wayland-protocols
|
||||||
|
CFLAGS += $(shell pkg-config --cflags $(PKG_DEPS))
|
||||||
|
LDFLAGS += $(shell pkg-config --libs $(PKG_DEPS))
|
||||||
|
|
||||||
|
# Protocol files
|
||||||
|
PROTOCOL_XML = $(PROTOCOLDIR)/xdg-shell.xml $(PROTOCOLDIR)/wlr-layer-shell-unstable-v1.xml
|
||||||
|
PROTOCOL_HEADERS = $(PROTOCOL_XML:$(PROTOCOLDIR)/%.xml=$(INCDIR)/%.h)
|
||||||
|
PROTOCOL_SOURCES = $(PROTOCOL_XML:$(PROTOCOLDIR)/%.xml=$(SRCDIR)/%.c)
|
||||||
|
PROTOCOL_OBJECTS = $(PROTOCOL_SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
|
||||||
|
|
||||||
|
# Additional libraries
|
||||||
|
LDFLAGS += -lm -ldl
|
||||||
|
|
||||||
|
# Source files (excluding generated protocol files)
|
||||||
|
SOURCES = $(filter-out $(PROTOCOL_SOURCES), $(wildcard $(SRCDIR)/*.c))
|
||||||
|
OBJECTS = $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o) $(PROTOCOL_OBJECTS)
|
||||||
|
DEPENDS = $(OBJECTS:.o=.d)
|
||||||
|
|
||||||
|
# Default target
|
||||||
|
TARGET = $(BINDIR)/$(PROJECT_NAME)
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
# Create directories
|
||||||
|
$(OBJDIR):
|
||||||
|
@mkdir -p $(OBJDIR)
|
||||||
|
|
||||||
|
$(BINDIR):
|
||||||
|
@mkdir -p $(BINDIR)
|
||||||
|
|
||||||
|
# Protocol generation
|
||||||
|
$(INCDIR)/%.h: $(PROTOCOLDIR)/%.xml | $(INCDIR)
|
||||||
|
@echo " PROTO $<"
|
||||||
|
@wayland-scanner client-header $< $@
|
||||||
|
|
||||||
|
$(SRCDIR)/%.c: $(PROTOCOLDIR)/%.xml
|
||||||
|
@echo " PROTO $<"
|
||||||
|
@wayland-scanner private-code $< $@
|
||||||
|
|
||||||
|
# Make sure include directory exists
|
||||||
|
$(INCDIR):
|
||||||
|
@mkdir -p $(INCDIR)
|
||||||
|
|
||||||
|
# Build main executable
|
||||||
|
$(TARGET): $(PROTOCOL_HEADERS) $(OBJECTS) | $(BINDIR)
|
||||||
|
@echo " LINK $@"
|
||||||
|
@$(CC) $(OBJECTS) -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
# Compile source files
|
||||||
|
$(OBJDIR)/%.o: $(SRCDIR)/%.c $(PROTOCOL_HEADERS) | $(OBJDIR)
|
||||||
|
@echo " CC $<"
|
||||||
|
@$(CC) $(CPPFLAGS) $(CFLAGS) -MMD -MP -c $< -o $@
|
||||||
|
|
||||||
|
# Debug build
|
||||||
|
debug: CFLAGS = $(DEBUG_CFLAGS)
|
||||||
|
debug: $(TARGET)
|
||||||
|
|
||||||
|
# Static build (for distribution)
|
||||||
|
static: LDFLAGS += -static
|
||||||
|
static: $(TARGET)
|
||||||
|
|
||||||
|
# Check if required dependencies are available
|
||||||
|
check-deps:
|
||||||
|
@echo "Checking dependencies..."
|
||||||
|
@pkg-config --exists $(PKG_DEPS) || (echo "Missing dependencies. Install: wayland-protocols libwayland-dev libegl1-mesa-dev libgl1-mesa-dev wayland-scanner" && exit 1)
|
||||||
|
@which wayland-scanner >/dev/null || (echo "wayland-scanner not found. Please install wayland-scanner." && exit 1)
|
||||||
|
@echo "All dependencies found."
|
||||||
|
|
||||||
|
# Install
|
||||||
|
install: $(TARGET)
|
||||||
|
@echo "Installing $(PROJECT_NAME)..."
|
||||||
|
install -Dm755 $(TARGET) $(DESTDIR)$(BINDIR_INSTALL)/$(PROJECT_NAME)
|
||||||
|
@if [ -f $(SYSTEMD_DIR)/$(PROJECT_NAME).service ]; then \
|
||||||
|
echo "Installing systemd service..."; \
|
||||||
|
install -Dm644 $(SYSTEMD_DIR)/$(PROJECT_NAME).service $(DESTDIR)$(SYSTEMD_INSTALL)/$(PROJECT_NAME).service; \
|
||||||
|
fi
|
||||||
|
@echo "Installation complete."
|
||||||
|
|
||||||
|
# Uninstall
|
||||||
|
uninstall:
|
||||||
|
@echo "Uninstalling $(PROJECT_NAME)..."
|
||||||
|
rm -f $(DESTDIR)$(BINDIR_INSTALL)/$(PROJECT_NAME)
|
||||||
|
rm -f $(DESTDIR)$(SYSTEMD_INSTALL)/$(PROJECT_NAME).service
|
||||||
|
@echo "Uninstall complete."
|
||||||
|
|
||||||
|
# Create systemd service file
|
||||||
|
systemd-service: $(SYSTEMD_DIR)/$(PROJECT_NAME).service
|
||||||
|
|
||||||
|
|
||||||
|
# Clean build artifacts
|
||||||
|
clean:
|
||||||
|
@echo "Cleaning build artifacts..."
|
||||||
|
rm -rf $(OBJDIR) $(BINDIR)
|
||||||
|
rm -f $(PROTOCOL_HEADERS) $(PROTOCOL_SOURCES)
|
||||||
|
|
||||||
|
# Format source code (requires clang-format)
|
||||||
|
format:
|
||||||
|
@echo "Formatting source code..."
|
||||||
|
@find $(SRCDIR) $(INCDIR) -name "*.c" -o -name "*.h" | xargs clang-format -i
|
||||||
|
|
||||||
|
# Static analysis (requires cppcheck)
|
||||||
|
analyze:
|
||||||
|
@echo "Running static analysis..."
|
||||||
|
@cppcheck --enable=all --std=c11 --language=c \
|
||||||
|
--include=$(INCDIR) \
|
||||||
|
--suppress=missingIncludeSystem \
|
||||||
|
--quiet \
|
||||||
|
$(SRCDIR)
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
# FIXME: add tests
|
||||||
|
test: $(TARGET)
|
||||||
|
@echo "Running tests..."
|
||||||
|
@echo "Tests not implemented yet."
|
||||||
|
|
||||||
|
# Help target
|
||||||
|
help:
|
||||||
|
@echo "Available targets:"
|
||||||
|
@echo " all - Build the main executable (default)"
|
||||||
|
@echo " debug - Build with debug symbols and sanitizers"
|
||||||
|
@echo " static - Build statically linked executable"
|
||||||
|
@echo " check-deps - Check if all dependencies are available"
|
||||||
|
@echo " install - Install the executable and systemd service"
|
||||||
|
@echo " uninstall - Remove installed files"
|
||||||
|
@echo " clean - Remove build artifacts"
|
||||||
|
@echo " distclean - Remove all generated files"
|
||||||
|
@echo " format - Format source code (requires clang-format)"
|
||||||
|
@echo " analyze - Run static analysis (requires cppcheck)"
|
||||||
|
@echo " test - Run tests"
|
||||||
|
@echo " help - Show this help message"
|
||||||
|
@echo ""
|
||||||
|
@echo "Examples:"
|
||||||
|
@echo " make # Build with default settings"
|
||||||
|
@echo " make debug # Build debug version"
|
||||||
|
@echo " make PREFIX=/usr install # Install to /usr instead of /usr/local"
|
||||||
|
@echo " make CC=clang # Use clang instead of gcc"
|
||||||
|
|
||||||
|
# Include dependency files
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
# Phony targets
|
||||||
|
.PHONY: all debug static check-deps install uninstall systemd-service sample-config clean distclean format analyze test help
|
||||||
|
|
||||||
|
# Print variables
|
||||||
|
print-%:
|
||||||
|
@echo '$*=$($*)'
|
211
README.md
Normal file
211
README.md
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
# Chroma
|
||||||
|
|
||||||
|
A simple, lightweight and efficientt wallpaper daeemon for Wayland compositors
|
||||||
|
with multi-monitor & automatic hotplugging support. Born from my woes with
|
||||||
|
Hyprpaper, swaybg and most ironically SWWW, which turned out to be NOT a
|
||||||
|
solution to my Wayland wallpaper woes.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
I did not expect to be writing something too feature-rich, but I still ended up
|
||||||
|
with something relatively convoluted. Chroma (mostly) reliably supports:
|
||||||
|
|
||||||
|
- **Multi-monitors**: Automatically detects and manages wallpapers for all
|
||||||
|
connected displays
|
||||||
|
- **Hotplugging**: Dynamically handles monitor connection/disconnection events
|
||||||
|
- **Per-output configuration**: Set different wallpapers for each monitor
|
||||||
|
- **Multiple image formats**: Supports JPEG, PNG, BMP, TGA, PSD, GIF, HDR, PIC,
|
||||||
|
PPM, PGM
|
||||||
|
- **EGL/OpenGL rendering**: Hardware-accelerated wallpaper rendering
|
||||||
|
- **Configuration file support**: Easy setup with INI-style config files
|
||||||
|
- **Signal handling**: Graceful shutdown and configuration reload (SIGHUP)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
#### Dependencies
|
||||||
|
|
||||||
|
- **Wayland**: wayland-client, wayland-egl
|
||||||
|
- **Graphics**: EGL, OpenGL
|
||||||
|
- **System**: glibc, libm, libdl
|
||||||
|
|
||||||
|
#### Development Dependencies
|
||||||
|
|
||||||
|
- GCC or Clang compiler
|
||||||
|
- Make
|
||||||
|
- pkg-config
|
||||||
|
- Wayland development headers
|
||||||
|
- EGL/OpenGL development headers
|
||||||
|
|
||||||
|
### Building
|
||||||
|
|
||||||
|
#### Quick Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check dependencies
|
||||||
|
make check-deps
|
||||||
|
|
||||||
|
# Build the daemon
|
||||||
|
make
|
||||||
|
|
||||||
|
# Or build debug version
|
||||||
|
make debug
|
||||||
|
```
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install to /usr/local (default)
|
||||||
|
sudo make install
|
||||||
|
|
||||||
|
# Install to /usr
|
||||||
|
sudo make PREFIX=/usr install
|
||||||
|
|
||||||
|
# Create systemd service file
|
||||||
|
make systemd-service
|
||||||
|
|
||||||
|
# Create sample configuration
|
||||||
|
make sample-config
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
#### Configuration File
|
||||||
|
|
||||||
|
Chroma looks for configuration files in this order:
|
||||||
|
|
||||||
|
1. `~/.config/chroma/chroma.conf`
|
||||||
|
2. `$XDG_CONFIG_HOME/chroma/chroma.conf`
|
||||||
|
3. `./chroma.conf` (current directory)
|
||||||
|
|
||||||
|
#### Sample Configuration
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# Default wallpaper for outputs without specific mapping
|
||||||
|
default_image = ~/.config/chroma/default.jpg
|
||||||
|
|
||||||
|
# Output-specific wallpapers
|
||||||
|
# Format: output.OUTPUT_NAME = /path/to/image.jpg
|
||||||
|
output.DP-1 = ~/Pictures/monitor1.jpg
|
||||||
|
output.DP-2 = ~/Pictures/monitor2.png
|
||||||
|
output.HDMI-A-1 = ~/Pictures/hdmi_wallpaper.jpg
|
||||||
|
```
|
||||||
|
|
||||||
|
### Finding Output Names
|
||||||
|
|
||||||
|
Use `wlr-randr` or similar tools to find your output names:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wlr-randr
|
||||||
|
```
|
||||||
|
|
||||||
|
### Command Line Options
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
Usage: chroma [OPTIONS]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-c, --config FILE Configuration file path
|
||||||
|
-d, --daemon Run as daemon
|
||||||
|
-v, --verbose Verbose logging
|
||||||
|
-h, --help Show help
|
||||||
|
--version Show version information
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
chroma -c ~/.config/chroma/chroma.conf
|
||||||
|
chroma --daemon
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running Manually
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run in foreground
|
||||||
|
chroma
|
||||||
|
|
||||||
|
# Run as daemon
|
||||||
|
chroma --daemon
|
||||||
|
|
||||||
|
# Use custom config
|
||||||
|
chroma -c /path/to/config.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
### Systemd Service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enable and start the service
|
||||||
|
systemctl --user enable chroma.service
|
||||||
|
systemctl --user start chroma.service
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
systemctl --user status chroma.service
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
journalctl --user -u chroma.service -f
|
||||||
|
|
||||||
|
# Reload configuration
|
||||||
|
systemctl --user reload chroma.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Auto-start with Desktop Session
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enable the service for auto-start
|
||||||
|
systemctl --user enable chroma.service
|
||||||
|
|
||||||
|
# The service will start automatically with your graphical session
|
||||||
|
```
|
||||||
|
|
||||||
|
### Supported Wayland Compositors
|
||||||
|
|
||||||
|
Chroma works with any Wayland compositor that supports:
|
||||||
|
|
||||||
|
- `wl_compositor` interface
|
||||||
|
- `wl_output` interface
|
||||||
|
- EGL window surface creation
|
||||||
|
|
||||||
|
Tested only on Hyprland.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Building Debug Version
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make debug
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Formatting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make format # requires clang-format
|
||||||
|
```
|
||||||
|
|
||||||
|
### Static Analysis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make analyze # requires cppcheck
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
1. Fork the repository
|
||||||
|
2. Create a feature branch
|
||||||
|
3. Make your changes
|
||||||
|
4. Test thoroughly
|
||||||
|
5. Submit a pull request
|
||||||
|
|
||||||
|
### Code Style
|
||||||
|
|
||||||
|
- C11 standard
|
||||||
|
- 2-space indentation
|
||||||
|
- No tabs
|
||||||
|
- Function names: `chroma_function_name`
|
||||||
|
- Constants: `CHROMA_CONSTANT_NAME`
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
<!--markdownlint-disable MD059 -->
|
||||||
|
|
||||||
|
This project is made available under Mozilla Public License (MPL) version 2.0.
|
||||||
|
See [LICENSE](LICENSE) for more details on the exact conditions. An online copy
|
||||||
|
is provided [here](https://www.mozilla.org/en-US/MPL/2.0/).
|
48
chroma.conf.sample
Normal file
48
chroma.conf.sample
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# This is a sample configuration file for the Chroma wallpaper daemon.
|
||||||
|
# Lines starting with # are comments and are ignored.
|
||||||
|
#
|
||||||
|
# Configuration file locations (checked in order):
|
||||||
|
# 1. ~/.config/chroma/chroma.conf
|
||||||
|
# 2. $XDG_CONFIG_HOME/chroma/chroma.conf
|
||||||
|
# 3. ./chroma.conf (current directory)
|
||||||
|
#
|
||||||
|
# To get started:
|
||||||
|
# 1. Copy this file to ~/.config/chroma/chroma.conf
|
||||||
|
# 2. Modify the paths to point to your wallpaper images
|
||||||
|
# 3. Use 'wlr-randr' or similar tools to find your output names
|
||||||
|
# 4. Restart chroma or send SIGHUP to reload configuration
|
||||||
|
|
||||||
|
# This image will be used for any output that doesn't have a specific mapping.
|
||||||
|
# Supports: JPEG, PNG, BMP, TGA, PSD, GIF, HDR, PIC, PPM, PGM
|
||||||
|
# Paths can be absolute or relative, ~ expansion is supported.
|
||||||
|
#
|
||||||
|
# Alternative examples:
|
||||||
|
# default_image = ~/Pictures/wallpapers/default.png
|
||||||
|
# default_image = /usr/share/wallpapers/default.jpg
|
||||||
|
# default_image = ./wallpapers/fallback.jpg
|
||||||
|
default_image = ~/.config/chroma/default.jpg
|
||||||
|
|
||||||
|
|
||||||
|
# Whether to run as a background daemon
|
||||||
|
# Usually set via command line option --daemon, but can be set here too
|
||||||
|
daemon_mode = false
|
||||||
|
|
||||||
|
# Output-specific wallpaper mappings
|
||||||
|
# ==================================
|
||||||
|
# Format: output.OUTPUT_NAME = /path/to/image.ext
|
||||||
|
#
|
||||||
|
# To find your output names, run one of these commands:
|
||||||
|
# - wlr-randr (for wlroots-based compositors like Sway, Hyprland)
|
||||||
|
# - wayland-info | grep wl_output
|
||||||
|
# - kanshi list-outputs
|
||||||
|
#
|
||||||
|
# Common output name patterns:
|
||||||
|
# - DP-1, DP-2, DP-3, etc. (DisplayPort)
|
||||||
|
# - HDMI-A-1, HDMI-A-2, etc. (HDMI)
|
||||||
|
# - eDP-1 (embedded DisplayPort, laptops)
|
||||||
|
# - DVI-D-1, DVI-I-1 (DVI)
|
||||||
|
# - VGA-1 (VGA, legacy)
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# output.HDMI-A-1 = ~/Pictures/wallpaper.jpg
|
||||||
|
|
26
flake.lock
generated
Normal file
26
flake.lock
generated
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1759155593,
|
||||||
|
"narHash": "sha256-potIJyEY7ExgfiMzr44/KBODFednyzRUAE2vs4aThHs=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "13ca7febc86978bdb67c0ae94f568b189ae84eef",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
22
flake.nix
Normal file
22
flake.nix
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
description = "Wayland Wallpaper Daemon";
|
||||||
|
|
||||||
|
inputs.nixpkgs.url = "github:NixOS/nixpkgs?ref?nixos-unstable";
|
||||||
|
outputs = {nixpkgs, ...}: let
|
||||||
|
systems = ["x86_64-linux" "aarch64-linux"];
|
||||||
|
forAllSystems = f:
|
||||||
|
builtins.listToAttrs (map (system: {
|
||||||
|
name = system;
|
||||||
|
value = f system;
|
||||||
|
})
|
||||||
|
systems);
|
||||||
|
|
||||||
|
pkgsFor = system: nixpkgs.legacyPackages.${system};
|
||||||
|
in {
|
||||||
|
devShells = forAllSystems (system: let
|
||||||
|
pkgs = pkgsFor system;
|
||||||
|
in {
|
||||||
|
default = pkgs.callPackage ./shell.nix {};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
9
include/chroma.h
vendored
9
include/chroma.h
vendored
|
@ -62,6 +62,14 @@ typedef struct {
|
||||||
|
|
||||||
// Associated wallpaper
|
// Associated wallpaper
|
||||||
chroma_image_t *image;
|
chroma_image_t *image;
|
||||||
|
|
||||||
|
// OpenGL resource cache
|
||||||
|
GLuint texture_id;
|
||||||
|
GLuint shader_program;
|
||||||
|
GLuint vbo;
|
||||||
|
GLuint ebo;
|
||||||
|
bool gl_resources_initialized;
|
||||||
|
bool texture_uploaded;
|
||||||
} chroma_output_t;
|
} chroma_output_t;
|
||||||
|
|
||||||
// Config mapping structure
|
// Config mapping structure
|
||||||
|
@ -150,6 +158,7 @@ void chroma_egl_cleanup(chroma_state_t *state);
|
||||||
int chroma_surface_create(chroma_state_t *state, chroma_output_t *output);
|
int chroma_surface_create(chroma_state_t *state, chroma_output_t *output);
|
||||||
void chroma_surface_destroy(chroma_output_t *output);
|
void chroma_surface_destroy(chroma_output_t *output);
|
||||||
int chroma_render_wallpaper(chroma_state_t *state, chroma_output_t *output);
|
int chroma_render_wallpaper(chroma_state_t *state, chroma_output_t *output);
|
||||||
|
void chroma_output_invalidate_texture(chroma_output_t *output);
|
||||||
|
|
||||||
// Layer shell functions
|
// Layer shell functions
|
||||||
void chroma_layer_surface_configure(void *data,
|
void chroma_layer_surface_configure(void *data,
|
||||||
|
|
51
shell.nix
Normal file
51
shell.nix
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
{pkgs ? import <nixpkgs> {}}:
|
||||||
|
pkgs.mkShell {
|
||||||
|
name = "chroma";
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
gnumake
|
||||||
|
|
||||||
|
# Optional development tools
|
||||||
|
gdb
|
||||||
|
valgrind
|
||||||
|
strace
|
||||||
|
|
||||||
|
# Code formatting and analysis
|
||||||
|
clang-tools # includes clang-format
|
||||||
|
cppcheck
|
||||||
|
|
||||||
|
# Wayland libraries
|
||||||
|
wayland.dev
|
||||||
|
wayland-protocols
|
||||||
|
wayland-scanner
|
||||||
|
libxkbcommon
|
||||||
|
|
||||||
|
# EGL/OpenGL libraries
|
||||||
|
libGL
|
||||||
|
mesa
|
||||||
|
|
||||||
|
# System libraries
|
||||||
|
glibc.dev
|
||||||
|
];
|
||||||
|
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
pkg-config
|
||||||
|
];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
echo "Available commands:"
|
||||||
|
echo " make - Build the project"
|
||||||
|
echo " make debug - Build with debug symbols"
|
||||||
|
echo " make clean - Clean build artifacts"
|
||||||
|
echo " make install - Install to ~/.local"
|
||||||
|
echo " make help - Show all available targets"
|
||||||
|
echo
|
||||||
|
echo "Run 'make check-deps' to verify all dependencies are available."
|
||||||
|
echo
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Environment variables for the build system
|
||||||
|
env = {
|
||||||
|
CHROMA_VERSION = "1.0.0";
|
||||||
|
WAYLAND_DEBUG = 1;
|
||||||
|
};
|
||||||
|
}
|
|
@ -87,6 +87,14 @@ static int assign_wallpaper_to_output(chroma_state_t *state,
|
||||||
return CHROMA_ERROR_IMAGE;
|
return CHROMA_ERROR_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if image changed and invalidate texture cache if neceessary
|
||||||
|
bool image_changed = (output->image != image);
|
||||||
|
if (image_changed && output->image) {
|
||||||
|
chroma_output_invalidate_texture(output);
|
||||||
|
chroma_log("DEBUG", "Image changed for output %u, invalidated texture",
|
||||||
|
output->id);
|
||||||
|
}
|
||||||
|
|
||||||
// Assign image to output
|
// Assign image to output
|
||||||
output->image = image;
|
output->image = image;
|
||||||
|
|
||||||
|
|
45
src/image.c
45
src/image.c
|
@ -49,11 +49,12 @@ int chroma_image_load(chroma_image_t *image, const char *path) {
|
||||||
(double)file_size / (1024.0 * 1024.0));
|
(double)file_size / (1024.0 * 1024.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load image data using stb_image
|
// Load image data using stb_image, force RGBA format to avoid conversion
|
||||||
stbi_set_flip_vertically_on_load(0); // Keep images right-side up
|
stbi_set_flip_vertically_on_load(0); // keep images right-side up
|
||||||
|
|
||||||
image->data =
|
image->data =
|
||||||
stbi_load(path, &image->width, &image->height, &image->channels, 0);
|
stbi_load(path, &image->width, &image->height, &image->channels, 4);
|
||||||
|
image->channels = 4; // always RGBA after forced conversion
|
||||||
if (!image->data) {
|
if (!image->data) {
|
||||||
chroma_log("ERROR", "Failed to load image %s: %s", path,
|
chroma_log("ERROR", "Failed to load image %s: %s", path,
|
||||||
stbi_failure_reason());
|
stbi_failure_reason());
|
||||||
|
@ -68,39 +69,14 @@ int chroma_image_load(chroma_image_t *image, const char *path) {
|
||||||
return CHROMA_ERROR_IMAGE;
|
return CHROMA_ERROR_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check supported formats
|
// Validate we have RGBA data (should always be true with forced conversion)
|
||||||
if (image->channels < 3 || image->channels > 4) {
|
if (image->channels != 4) {
|
||||||
chroma_log("ERROR",
|
chroma_log("ERROR", "Failed to load image as RGBA: got %d channels",
|
||||||
"Unsupported image format: %d channels (need RGB or RGBA)",
|
|
||||||
image->channels);
|
image->channels);
|
||||||
chroma_image_free(image);
|
chroma_image_free(image);
|
||||||
return CHROMA_ERROR_IMAGE;
|
return CHROMA_ERROR_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert RGB to RGBA if necessary for consistent handling
|
|
||||||
if (image->channels == 3) {
|
|
||||||
int pixel_count = image->width * image->height;
|
|
||||||
unsigned char *rgba_data = malloc(pixel_count * 4);
|
|
||||||
if (!rgba_data) {
|
|
||||||
chroma_log("ERROR", "Failed to allocate memory for RGBA conversion");
|
|
||||||
chroma_image_free(image);
|
|
||||||
return CHROMA_ERROR_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert RGB to RGBA
|
|
||||||
for (int i = 0; i < pixel_count; i++) {
|
|
||||||
rgba_data[i * 4 + 0] = image->data[i * 3 + 0]; // R
|
|
||||||
rgba_data[i * 4 + 1] = image->data[i * 3 + 1]; // G
|
|
||||||
rgba_data[i * 4 + 2] = image->data[i * 3 + 2]; // B
|
|
||||||
rgba_data[i * 4 + 3] = 255; // A
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace original data
|
|
||||||
stbi_image_free(image->data);
|
|
||||||
image->data = rgba_data;
|
|
||||||
image->channels = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
image->loaded = true;
|
image->loaded = true;
|
||||||
|
|
||||||
chroma_log("INFO", "Loaded image: %s (%dx%d, %d channels, %.2f MB)", path,
|
chroma_log("INFO", "Loaded image: %s (%dx%d, %d channels, %.2f MB)", path,
|
||||||
|
@ -118,13 +94,8 @@ void chroma_image_free(chroma_image_t *image) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image->data) {
|
if (image->data) {
|
||||||
if (image->channels == 4 && strlen(image->path) > 0) {
|
// Always use stbi_image_free since we load directly with stbi_load
|
||||||
// If we converted from RGB to RGBA, use regular free()
|
|
||||||
free(image->data);
|
|
||||||
} else {
|
|
||||||
// If loaded directly by stb_image, use stbi_image_free()
|
|
||||||
stbi_image_free(image->data);
|
stbi_image_free(image->data);
|
||||||
}
|
|
||||||
image->data = NULL;
|
image->data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
265
src/render.c
265
src/render.c
|
@ -1,4 +1,3 @@
|
||||||
#include <math.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -7,6 +6,7 @@
|
||||||
#include <GLES2/gl2.h>
|
#include <GLES2/gl2.h>
|
||||||
|
|
||||||
#include "../include/chroma.h"
|
#include "../include/chroma.h"
|
||||||
|
#include "../include/stb_image.h"
|
||||||
|
|
||||||
// Vertex shader for simple texture rendering
|
// Vertex shader for simple texture rendering
|
||||||
static const char *vertex_shader_source =
|
static const char *vertex_shader_source =
|
||||||
|
@ -31,18 +31,17 @@ static const char *fragment_shader_source =
|
||||||
// Vertices for a fullscreen quad
|
// Vertices for a fullscreen quad
|
||||||
static const float vertices[] = {
|
static const float vertices[] = {
|
||||||
// Position Texcoord
|
// Position Texcoord
|
||||||
-1.0f, -1.0f, 0.0f, 1.0f, // Bottom left
|
-1.0f, -1.0f, 0.0f, 1.0f, // bottom left
|
||||||
1.0f, -1.0f, 1.0f, 1.0f, // Bottom right
|
1.0f, -1.0f, 1.0f, 1.0f, // bottom right
|
||||||
1.0f, 1.0f, 1.0f, 0.0f, // Top right
|
1.0f, 1.0f, 1.0f, 0.0f, // top right
|
||||||
-1.0f, 1.0f, 0.0f, 0.0f, // Top left
|
-1.0f, 1.0f, 0.0f, 0.0f, // top left
|
||||||
};
|
};
|
||||||
|
|
||||||
static const unsigned int indices[] = {
|
static const unsigned int indices[] = {
|
||||||
0, 1, 2, // First triangle
|
0, 1, 2, // first triangle
|
||||||
2, 3, 0 // Second triangle
|
2, 3, 0 // second triangle
|
||||||
};
|
};
|
||||||
|
|
||||||
// Shader compilation helper
|
|
||||||
static GLuint compile_shader(GLenum type, const char *source) {
|
static GLuint compile_shader(GLenum type, const char *source) {
|
||||||
GLuint shader = glCreateShader(type);
|
GLuint shader = glCreateShader(type);
|
||||||
if (!shader) {
|
if (!shader) {
|
||||||
|
@ -66,7 +65,6 @@ static GLuint compile_shader(GLenum type, const char *source) {
|
||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shader program creation helper
|
|
||||||
static GLuint create_shader_program(void) {
|
static GLuint create_shader_program(void) {
|
||||||
GLuint vertex_shader = compile_shader(GL_VERTEX_SHADER, vertex_shader_source);
|
GLuint vertex_shader = compile_shader(GL_VERTEX_SHADER, vertex_shader_source);
|
||||||
if (!vertex_shader) {
|
if (!vertex_shader) {
|
||||||
|
@ -108,6 +106,145 @@ static GLuint create_shader_program(void) {
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize OpenGL resources for output
|
||||||
|
static int init_gl_resources(chroma_output_t *output) {
|
||||||
|
if (!output || output->gl_resources_initialized) {
|
||||||
|
return CHROMA_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create shader prog
|
||||||
|
output->shader_program = create_shader_program();
|
||||||
|
if (!output->shader_program) {
|
||||||
|
chroma_log("ERROR", "Failed to create shader program for output %u",
|
||||||
|
output->id);
|
||||||
|
return CHROMA_ERROR_EGL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and setup VBO/EBO
|
||||||
|
glGenBuffers(1, &output->vbo);
|
||||||
|
glGenBuffers(1, &output->ebo);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, output->vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, output->ebo);
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices,
|
||||||
|
GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
output->texture_id = 0; // will be created when image is assigned
|
||||||
|
output->gl_resources_initialized = true;
|
||||||
|
|
||||||
|
chroma_log("DEBUG", "Initialized GL resources for output %u", output->id);
|
||||||
|
return CHROMA_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create or update texture from image data
|
||||||
|
static int update_texture_from_image(chroma_output_t *output,
|
||||||
|
chroma_image_t *image) {
|
||||||
|
if (!output || !image || !image->loaded) {
|
||||||
|
return CHROMA_ERROR_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If image data was already freed after previous GPU upload, we can't upload
|
||||||
|
// again
|
||||||
|
if (!image->data) {
|
||||||
|
chroma_log("ERROR",
|
||||||
|
"Cannot create texture: image data already freed for %s",
|
||||||
|
image->path);
|
||||||
|
return CHROMA_ERROR_IMAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete existing texture if it exists
|
||||||
|
if (output->texture_id != 0) {
|
||||||
|
glDeleteTextures(1, &output->texture_id);
|
||||||
|
output->texture_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new texture
|
||||||
|
glGenTextures(1, &output->texture_id);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, output->texture_id);
|
||||||
|
|
||||||
|
// Set texture parameters
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
|
// Upload texture data (always RGBA now)
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->width, image->height, 0,
|
||||||
|
GL_RGBA, GL_UNSIGNED_BYTE, image->data);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
// Mark this output as having uploaded its texture
|
||||||
|
output->texture_uploaded = true;
|
||||||
|
|
||||||
|
// Free system RAM copy only when ALL outputs using this image have uploaded
|
||||||
|
// to GPU
|
||||||
|
if (image->data) {
|
||||||
|
// Count total outputs using this image and how many have uploaded
|
||||||
|
int total_using = 0;
|
||||||
|
int uploaded_count = 0;
|
||||||
|
|
||||||
|
chroma_state_t *state = output->state;
|
||||||
|
for (int i = 0; i < state->output_count; i++) {
|
||||||
|
if (state->outputs[i].active && state->outputs[i].image == image) {
|
||||||
|
total_using++;
|
||||||
|
if (state->outputs[i].texture_uploaded) {
|
||||||
|
uploaded_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only free image data when ALL outputs using it have uploaded
|
||||||
|
if (total_using > 0 && uploaded_count >= total_using) {
|
||||||
|
size_t freed_bytes =
|
||||||
|
(size_t)image->width * image->height * image->channels;
|
||||||
|
stbi_image_free(image->data);
|
||||||
|
image->data = NULL;
|
||||||
|
chroma_log("INFO",
|
||||||
|
"Freed %.2f MB of image data after all %d outputs uploaded to "
|
||||||
|
"GPU: %s",
|
||||||
|
(double)freed_bytes / (1024.0 * 1024.0), total_using,
|
||||||
|
image->path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chroma_log("DEBUG", "Updated texture for output %u (%dx%d)", output->id,
|
||||||
|
image->width, image->height);
|
||||||
|
return CHROMA_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup OpenGL resources for output
|
||||||
|
static void cleanup_gl_resources(chroma_output_t *output) {
|
||||||
|
if (!output || !output->gl_resources_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output->texture_id != 0) {
|
||||||
|
glDeleteTextures(1, &output->texture_id);
|
||||||
|
output->texture_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output->shader_program != 0) {
|
||||||
|
glDeleteProgram(output->shader_program);
|
||||||
|
output->shader_program = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output->vbo != 0) {
|
||||||
|
glDeleteBuffers(1, &output->vbo);
|
||||||
|
output->vbo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output->ebo != 0) {
|
||||||
|
glDeleteBuffers(1, &output->ebo);
|
||||||
|
output->ebo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
output->gl_resources_initialized = false;
|
||||||
|
chroma_log("DEBUG", "Cleaned up GL resources for output %u", output->id);
|
||||||
|
}
|
||||||
|
|
||||||
// EGL configuration selection
|
// EGL configuration selection
|
||||||
static int choose_egl_config(EGLDisplay display, EGLConfig *config) {
|
static int choose_egl_config(EGLDisplay display, EGLConfig *config) {
|
||||||
EGLint attributes[] = {EGL_SURFACE_TYPE,
|
EGLint attributes[] = {EGL_SURFACE_TYPE,
|
||||||
|
@ -292,6 +429,9 @@ void chroma_surface_destroy(chroma_output_t *output) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean up OpenGL resources first
|
||||||
|
cleanup_gl_resources(output);
|
||||||
|
|
||||||
if (output->egl_surface != EGL_NO_SURFACE) {
|
if (output->egl_surface != EGL_NO_SURFACE) {
|
||||||
eglDestroySurface(eglGetCurrentDisplay(), output->egl_surface);
|
eglDestroySurface(eglGetCurrentDisplay(), output->egl_surface);
|
||||||
output->egl_surface = EGL_NO_SURFACE;
|
output->egl_surface = EGL_NO_SURFACE;
|
||||||
|
@ -315,33 +455,7 @@ void chroma_surface_destroy(chroma_output_t *output) {
|
||||||
chroma_log("DEBUG", "Destroyed surface for output %u", output->id);
|
chroma_log("DEBUG", "Destroyed surface for output %u", output->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create texture from image data
|
// Render wallpaper to output using cached resources
|
||||||
static GLuint create_texture_from_image(chroma_image_t *image) {
|
|
||||||
if (!image || !image->loaded || !image->data) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLuint texture;
|
|
||||||
glGenTextures(1, &texture);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
|
||||||
|
|
||||||
// Set texture parameters
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
||||||
|
|
||||||
// Upload texture data
|
|
||||||
GLenum format = (image->channels == 4) ? GL_RGBA : GL_RGB;
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, format, image->width, image->height, 0, format,
|
|
||||||
GL_UNSIGNED_BYTE, image->data);
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render wallpaper to output
|
|
||||||
int chroma_render_wallpaper(chroma_state_t *state, chroma_output_t *output) {
|
int chroma_render_wallpaper(chroma_state_t *state, chroma_output_t *output) {
|
||||||
if (!state || !output || !output->image || !output->image->loaded) {
|
if (!state || !output || !output->image || !output->image->loaded) {
|
||||||
return CHROMA_ERROR_INIT;
|
return CHROMA_ERROR_INIT;
|
||||||
|
@ -355,6 +469,17 @@ int chroma_render_wallpaper(chroma_state_t *state, chroma_output_t *output) {
|
||||||
return CHROMA_ERROR_EGL;
|
return CHROMA_ERROR_EGL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (init_gl_resources(output) != CHROMA_OK) {
|
||||||
|
return CHROMA_ERROR_EGL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output->texture_id == 0) {
|
||||||
|
if (update_texture_from_image(output, output->image) != CHROMA_OK) {
|
||||||
|
chroma_log("ERROR", "Failed to update texture for output %u", output->id);
|
||||||
|
return CHROMA_ERROR_EGL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set viewport
|
// Set viewport
|
||||||
glViewport(0, 0, output->width, output->height);
|
glViewport(0, 0, output->width, output->height);
|
||||||
|
|
||||||
|
@ -362,42 +487,21 @@ int chroma_render_wallpaper(chroma_state_t *state, chroma_output_t *output) {
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
// Create shader program (should be cached in real implementation)
|
// Use cached shader program
|
||||||
GLuint program = create_shader_program();
|
glUseProgram(output->shader_program);
|
||||||
if (!program) {
|
|
||||||
return CHROMA_ERROR_EGL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use shader program
|
|
||||||
glUseProgram(program);
|
|
||||||
|
|
||||||
// Create and bind texture
|
|
||||||
GLuint texture = create_texture_from_image(output->image);
|
|
||||||
if (!texture) {
|
|
||||||
chroma_log("ERROR", "Failed to create texture for output %u", output->id);
|
|
||||||
glDeleteProgram(program);
|
|
||||||
return CHROMA_ERROR_EGL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Bind cached texture
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
glBindTexture(GL_TEXTURE_2D, output->texture_id);
|
||||||
glUniform1i(glGetUniformLocation(program, "texture"), 0);
|
glUniform1i(glGetUniformLocation(output->shader_program, "texture"), 0);
|
||||||
|
|
||||||
// Create vertex buffer objects (should be cached in real implementation)
|
// Use cached VBO/EBO
|
||||||
GLuint vbo, ebo;
|
glBindBuffer(GL_ARRAY_BUFFER, output->vbo);
|
||||||
glGenBuffers(1, &vbo);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, output->ebo);
|
||||||
glGenBuffers(1, &ebo);
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
|
||||||
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices,
|
|
||||||
GL_STATIC_DRAW);
|
|
||||||
|
|
||||||
// Set vertex attributes
|
// Set vertex attributes
|
||||||
GLint position_attr = glGetAttribLocation(program, "position");
|
GLint position_attr = glGetAttribLocation(output->shader_program, "position");
|
||||||
GLint texcoord_attr = glGetAttribLocation(program, "texcoord");
|
GLint texcoord_attr = glGetAttribLocation(output->shader_program, "texcoord");
|
||||||
|
|
||||||
glEnableVertexAttribArray(position_attr);
|
glEnableVertexAttribArray(position_attr);
|
||||||
glVertexAttribPointer(position_attr, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float),
|
glVertexAttribPointer(position_attr, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float),
|
||||||
|
@ -410,11 +514,11 @@ int chroma_render_wallpaper(chroma_state_t *state, chroma_output_t *output) {
|
||||||
// Draw
|
// Draw
|
||||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
||||||
|
|
||||||
// Cleanup
|
// Unbind resources
|
||||||
glDeleteBuffers(1, &vbo);
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
glDeleteBuffers(1, &ebo);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||||
glDeleteTextures(1, &texture);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
glDeleteProgram(program);
|
glUseProgram(0);
|
||||||
|
|
||||||
// Swap buffers
|
// Swap buffers
|
||||||
if (!eglSwapBuffers(state->egl_display, output->egl_surface)) {
|
if (!eglSwapBuffers(state->egl_display, output->egl_surface)) {
|
||||||
|
@ -426,6 +530,21 @@ int chroma_render_wallpaper(chroma_state_t *state, chroma_output_t *output) {
|
||||||
// Commit surface
|
// Commit surface
|
||||||
wl_surface_commit(output->surface);
|
wl_surface_commit(output->surface);
|
||||||
|
|
||||||
chroma_log("DEBUG", "Rendered wallpaper to output %u", output->id);
|
chroma_log("DEBUG", "Rendered wallpaper to output %u (cached resources)",
|
||||||
|
output->id);
|
||||||
return CHROMA_OK;
|
return CHROMA_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Invalidate texture cache for output
|
||||||
|
void chroma_output_invalidate_texture(chroma_output_t *output) {
|
||||||
|
if (!output || !output->gl_resources_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output->texture_id != 0) {
|
||||||
|
glDeleteTextures(1, &output->texture_id);
|
||||||
|
output->texture_id = 0;
|
||||||
|
output->texture_uploaded = false; // reset upload flag
|
||||||
|
chroma_log("DEBUG", "Invalidated texture cache for output %u", output->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
24
systemd/chroma.service
Normal file
24
systemd/chroma.service
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Chroma Wallpaper Daemon
|
||||||
|
After=graphical-session.target
|
||||||
|
Wants=graphical-session.target
|
||||||
|
PartOf=graphical-session.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=chroma
|
||||||
|
ExecReload=kill -HUP $MAINPID
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=1
|
||||||
|
KillMode=mixed
|
||||||
|
TimeoutStopSec=5
|
||||||
|
|
||||||
|
# Security
|
||||||
|
NoNewPrivileges=true
|
||||||
|
PrivateDevices=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=read-only
|
||||||
|
ReadWritePaths=%h/.config/chroma
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-useer.target
|
Loading…
Add table
Add a link
Reference in a new issue