mirror of
https://codeberg.org/dnkl/yambar.git
synced 2025-04-23 20:35:42 +02:00
particles/icon: init
Introduce a new icon particle. It follows the icon spec (https://specifications.freedesktop.org/icon-theme-spec/latest/index.html). Rendering logic is taken from fuzzel (using nanosvg + libpng), while loading logic is taken from sway. Standard usage is with `use-tag = false` which expands the provided string template and then loads the string as the icon name. There are settings to manually override the base paths, themes, etc. The second usage which is required for tray support is a special icon tag that transfers raw pixmaps. With `use-tag = true` it first expands the string, and then uses that output to find an icon pixmap tag. To reduce memory usage, themes are reference counted so they can be passed down the configuration stack without having to load them in multiple times. For programmability, a fallback particle can be specified if no icon/tag is found `fallback: ...`. And the new icon pixmap tag can be existence checked in map conditions using `+{tag_name}`. Future work to be done in follow up diffs: 1. Icon caching. Currently performs an icon lookup on each instantiation & a render on each refresh. 2. Theme caching. Changing theme directories results in a new "theme collection" being created resulting in the possibility of duplicated theme loading.
This commit is contained in:
parent
1a323c6d21
commit
6113f9b94e
51 changed files with 8473 additions and 40 deletions
75
3rd-party/nanosvg/CMakeLists.txt
vendored
Normal file
75
3rd-party/nanosvg/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
|
project(NanoSVG C)
|
||||||
|
|
||||||
|
# CMake needs *.c files to do something useful
|
||||||
|
configure_file(src/nanosvg.h ${CMAKE_CURRENT_BINARY_DIR}/nanosvg.c)
|
||||||
|
configure_file(src/nanosvgrast.h ${CMAKE_CURRENT_BINARY_DIR}/nanosvgrast.c)
|
||||||
|
|
||||||
|
add_library(nanosvg ${CMAKE_CURRENT_BINARY_DIR}/nanosvg.c)
|
||||||
|
|
||||||
|
find_library(MATH_LIBRARY m) # Business as usual
|
||||||
|
if(MATH_LIBRARY)
|
||||||
|
target_link_libraries(nanosvg PUBLIC ${MATH_LIBRARY})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_include_directories(nanosvg PUBLIC $<INSTALL_INTERFACE:include/nanosvg>)
|
||||||
|
target_compile_definitions(nanosvg PRIVATE NANOSVG_IMPLEMENTATION)
|
||||||
|
|
||||||
|
# Same for nanosvgrast
|
||||||
|
add_library(nanosvgrast ${CMAKE_CURRENT_BINARY_DIR}/nanosvgrast.c)
|
||||||
|
target_link_libraries(nanosvgrast PUBLIC nanosvg)
|
||||||
|
target_include_directories(nanosvgrast PRIVATE src)
|
||||||
|
target_compile_definitions(nanosvgrast PRIVATE NANOSVGRAST_IMPLEMENTATION)
|
||||||
|
|
||||||
|
# Installation and export:
|
||||||
|
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
|
||||||
|
write_basic_package_version_file(
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||||
|
VERSION 1.0
|
||||||
|
COMPATIBILITY AnyNewerVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS nanosvg nanosvgrast
|
||||||
|
EXPORT ${PROJECT_NAME}Targets
|
||||||
|
)
|
||||||
|
|
||||||
|
export(EXPORT ${PROJECT_NAME}Targets
|
||||||
|
FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake"
|
||||||
|
NAMESPACE ${PROJECT_NAME}::
|
||||||
|
)
|
||||||
|
|
||||||
|
set(ConfigPackageLocation lib/cmake/${PROJECT_NAME})
|
||||||
|
|
||||||
|
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||||
|
INSTALL_DESTINATION ${ConfigPackageLocation}
|
||||||
|
NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES
|
||||||
|
src/nanosvg.h
|
||||||
|
src/nanosvgrast.h
|
||||||
|
DESTINATION
|
||||||
|
include/nanosvg
|
||||||
|
)
|
||||||
|
|
||||||
|
install(EXPORT ${PROJECT_NAME}Targets
|
||||||
|
FILE
|
||||||
|
${PROJECT_NAME}Targets.cmake
|
||||||
|
NAMESPACE
|
||||||
|
${PROJECT_NAME}::
|
||||||
|
DESTINATION
|
||||||
|
${ConfigPackageLocation}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||||
|
DESTINATION
|
||||||
|
${ConfigPackageLocation}
|
||||||
|
)
|
5
3rd-party/nanosvg/Config.cmake.in
vendored
Normal file
5
3rd-party/nanosvg/Config.cmake.in
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
if (EXISTS ${CMAKE_CURRENT_LIST_DIR}/NanoSVGTargets.cmake)
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/NanoSVGTargets.cmake")
|
||||||
|
endif ()
|
18
3rd-party/nanosvg/LICENSE.txt
vendored
Normal file
18
3rd-party/nanosvg/LICENSE.txt
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
Copyright (c) 2013-14 Mikko Mononen memon@inside.org
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
112
3rd-party/nanosvg/README.md
vendored
Normal file
112
3rd-party/nanosvg/README.md
vendored
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
*This project is not actively maintained.*
|
||||||
|
|
||||||
|
Nano SVG
|
||||||
|
==========
|
||||||
|
|
||||||
|
## Parser
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes.
|
||||||
|
|
||||||
|
The library suits well for anything from rendering scalable icons in your editor application to prototyping a game.
|
||||||
|
|
||||||
|
NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request!
|
||||||
|
|
||||||
|
The shapes in the SVG images are transformed by the viewBox and converted to specified units.
|
||||||
|
That is, you should get the same looking data as your designed in your favorite app.
|
||||||
|
|
||||||
|
NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose
|
||||||
|
to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters.
|
||||||
|
|
||||||
|
The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'.
|
||||||
|
DPI (dots-per-inch) controls how the unit conversion is done.
|
||||||
|
|
||||||
|
If you don't know or care about the units stuff, "px" and 96 should get you going.
|
||||||
|
|
||||||
|
## Rasterizer
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The parser library is accompanied with really simpler SVG rasterizer. Currently it only renders flat filled shapes.
|
||||||
|
|
||||||
|
The intended usage for the rasterizer is to for example bake icons of different size into a texture. The rasterizer is not particular fast or accurate, but it's small and packed in one header file.
|
||||||
|
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
``` C
|
||||||
|
// Load
|
||||||
|
struct NSVGimage* image;
|
||||||
|
image = nsvgParseFromFile("test.svg", "px", 96);
|
||||||
|
printf("size: %f x %f\n", image->width, image->height);
|
||||||
|
// Use...
|
||||||
|
for (shape = image->shapes; shape != NULL; shape = shape->next) {
|
||||||
|
for (path = shape->paths; path != NULL; path = path->next) {
|
||||||
|
for (i = 0; i < path->npts-1; i += 3) {
|
||||||
|
float* p = &path->pts[i*2];
|
||||||
|
drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Delete
|
||||||
|
nsvgDelete(image);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using NanoSVG in your project
|
||||||
|
|
||||||
|
In order to use NanoSVG in your own project, just copy nanosvg.h to your project.
|
||||||
|
In one C/C++ define `NANOSVG_IMPLEMENTATION` before including the library to expand the NanoSVG implementation in that file.
|
||||||
|
NanoSVG depends on `stdio.h` ,`string.h` and `math.h`, they should be included where the implementation is expanded before including NanoSVG.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#define NANOSVG_IMPLEMENTATION // Expands implementation
|
||||||
|
#include "nanosvg.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, NanoSVG parses only the most common colors. In order to get support for full list of [SVG color keywords](http://www.w3.org/TR/SVG11/types.html#ColorKeywords), define `NANOSVG_ALL_COLOR_KEYWORDS` before expanding the implementation.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#define NANOSVG_ALL_COLOR_KEYWORDS // Include full list of color keywords.
|
||||||
|
#define NANOSVG_IMPLEMENTATION // Expands implementation
|
||||||
|
#include "nanosvg.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can install the library using CMake and import it into your project using the standard CMake `find_package` command.
|
||||||
|
|
||||||
|
```CMake
|
||||||
|
add_executable(myexe main.c)
|
||||||
|
|
||||||
|
find_package(NanoSVG REQUIRED)
|
||||||
|
|
||||||
|
target_link_libraries(myexe NanoSVG::nanosvg NanoSVG::nanosvgrast)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compiling Example Project
|
||||||
|
|
||||||
|
In order to compile the demo project, your will need to install [GLFW](http://www.glfw.org/) to compile.
|
||||||
|
|
||||||
|
NanoSVG demo project uses [premake4](http://industriousone.com/premake) to build platform specific projects, now is good time to install it if you don't have it already. To build the example, navigate into the root folder in your favorite terminal, then:
|
||||||
|
|
||||||
|
- *OS X*: `premake4 xcode4`
|
||||||
|
- *Windows*: `premake4 vs2010`
|
||||||
|
- *Linux*: `premake4 gmake`
|
||||||
|
|
||||||
|
See premake4 documentation for full list of supported build file types. The projects will be created in `build` folder. An example of building and running the example on OS X:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ premake4 gmake
|
||||||
|
$ cd build/
|
||||||
|
$ make
|
||||||
|
$ ./example
|
||||||
|
```
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
The library is licensed under [zlib license](LICENSE.txt)
|
730
3rd-party/nanosvg/example/23.svg
vendored
Normal file
730
3rd-party/nanosvg/example/23.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 94 KiB |
97
3rd-party/nanosvg/example/drawing.svg
vendored
Normal file
97
3rd-party/nanosvg/example/drawing.svg
vendored
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="1000"
|
||||||
|
height="1000"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.2 r9819"
|
||||||
|
sodipodi:docname="drawing.svg">
|
||||||
|
<defs
|
||||||
|
id="defs4" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="0.35"
|
||||||
|
inkscape:cx="375"
|
||||||
|
inkscape:cy="520"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:window-width="751"
|
||||||
|
inkscape:window-height="578"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="22"
|
||||||
|
inkscape:window-maximized="0" />
|
||||||
|
<metadata
|
||||||
|
id="metadata7">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-52.362183)">
|
||||||
|
<path
|
||||||
|
style="fill:#ff5555;stroke:#000000;stroke-width:10.62107277px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 131.73911,422.01626 c 0,146.85769 43.82213,215.39128 201.5818,141.96244 157.75968,-73.42885 188.43518,-107.69564 354.95926,78.32409 166.5241,186.01973 210.34624,244.76282 162.1419,-122.3814 -48.20435,-367.1442 -4.38221,34.26679 -131.46641,-24.47627 C 591.87149,436.70204 732.10231,191.93923 543.66715,187.04398 355.23198,182.14871 574.34264,265.36807 534.90271,368.16845 495.4628,470.96883 355.23198,627.61702 311.40985,475.8641 267.58772,324.11115 193.09009,333.90166 131.73911,422.01626 z"
|
||||||
|
id="path2985"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<rect
|
||||||
|
style="fill:#00ffff;stroke:#000000;stroke-width:10.62107277px;"
|
||||||
|
id="rect2987"
|
||||||
|
width="390.01697"
|
||||||
|
height="200.70551"
|
||||||
|
x="228.14781"
|
||||||
|
y="539.50238" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="arc"
|
||||||
|
style="fill:#00ffff"
|
||||||
|
id="path3008"
|
||||||
|
sodipodi:cx="157.14285"
|
||||||
|
sodipodi:cy="168.57143"
|
||||||
|
sodipodi:rx="57.142857"
|
||||||
|
sodipodi:ry="88.571426"
|
||||||
|
d="m 214.28571,168.57143 a 57.142857,88.571426 0 1 1 -114.285714,0 57.142857,88.571426 0 1 1 114.285714,0 z"
|
||||||
|
transform="translate(188.57143,138.07647)" />
|
||||||
|
<rect
|
||||||
|
style="fill:#00ff00"
|
||||||
|
id="rect3010"
|
||||||
|
width="371.42856"
|
||||||
|
height="145.71428"
|
||||||
|
x="261.66104"
|
||||||
|
y="945.44141"
|
||||||
|
transform="matrix(0.948958,-0.31540248,0.31540248,0.948958,0,0)"
|
||||||
|
ry="51.42857" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="arc"
|
||||||
|
style="fill:#00ff00"
|
||||||
|
id="path3038"
|
||||||
|
sodipodi:cx="200"
|
||||||
|
sodipodi:cy="177.14285"
|
||||||
|
sodipodi:rx="54.285713"
|
||||||
|
sodipodi:ry="54.285713"
|
||||||
|
d="m 254.28571,177.14285 a 54.285713,54.285713 0 1 1 -108.57142,0 54.285713,54.285713 0 1 1 108.57142,0 z"
|
||||||
|
transform="translate(0,52.362183)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.4 KiB |
258
3rd-party/nanosvg/example/example1.c
vendored
Normal file
258
3rd-party/nanosvg/example/example1.c
vendored
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013 Mikko Mononen memon@inside.org
|
||||||
|
//
|
||||||
|
// This software is provided 'as-is', without any express or implied
|
||||||
|
// warranty. In no event will the authors be held liable for any damages
|
||||||
|
// arising from the use of this software.
|
||||||
|
// Permission is granted to anyone to use this software for any purpose,
|
||||||
|
// including commercial applications, and to alter it and redistribute it
|
||||||
|
// freely, subject to the following restrictions:
|
||||||
|
// 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
// claim that you wrote the original software. If you use this software
|
||||||
|
// in a product, an acknowledgment in the product documentation would be
|
||||||
|
// appreciated but is not required.
|
||||||
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
// misrepresented as being the original software.
|
||||||
|
// 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <float.h>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#define NANOSVG_IMPLEMENTATION
|
||||||
|
#include "nanosvg.h"
|
||||||
|
|
||||||
|
NSVGimage* g_image = NULL;
|
||||||
|
|
||||||
|
static unsigned char bgColor[4] = {205,202,200,255};
|
||||||
|
static unsigned char lineColor[4] = {0,160,192,255};
|
||||||
|
|
||||||
|
static float distPtSeg(float x, float y, float px, float py, float qx, float qy)
|
||||||
|
{
|
||||||
|
float pqx, pqy, dx, dy, d, t;
|
||||||
|
pqx = qx-px;
|
||||||
|
pqy = qy-py;
|
||||||
|
dx = x-px;
|
||||||
|
dy = y-py;
|
||||||
|
d = pqx*pqx + pqy*pqy;
|
||||||
|
t = pqx*dx + pqy*dy;
|
||||||
|
if (d > 0) t /= d;
|
||||||
|
if (t < 0) t = 0;
|
||||||
|
else if (t > 1) t = 1;
|
||||||
|
dx = px + t*pqx - x;
|
||||||
|
dy = py + t*pqy - y;
|
||||||
|
return dx*dx + dy*dy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cubicBez(float x1, float y1, float x2, float y2,
|
||||||
|
float x3, float y3, float x4, float y4,
|
||||||
|
float tol, int level)
|
||||||
|
{
|
||||||
|
float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
|
||||||
|
float d;
|
||||||
|
|
||||||
|
if (level > 12) return;
|
||||||
|
|
||||||
|
x12 = (x1+x2)*0.5f;
|
||||||
|
y12 = (y1+y2)*0.5f;
|
||||||
|
x23 = (x2+x3)*0.5f;
|
||||||
|
y23 = (y2+y3)*0.5f;
|
||||||
|
x34 = (x3+x4)*0.5f;
|
||||||
|
y34 = (y3+y4)*0.5f;
|
||||||
|
x123 = (x12+x23)*0.5f;
|
||||||
|
y123 = (y12+y23)*0.5f;
|
||||||
|
x234 = (x23+x34)*0.5f;
|
||||||
|
y234 = (y23+y34)*0.5f;
|
||||||
|
x1234 = (x123+x234)*0.5f;
|
||||||
|
y1234 = (y123+y234)*0.5f;
|
||||||
|
|
||||||
|
d = distPtSeg(x1234, y1234, x1,y1, x4,y4);
|
||||||
|
if (d > tol*tol) {
|
||||||
|
cubicBez(x1,y1, x12,y12, x123,y123, x1234,y1234, tol, level+1);
|
||||||
|
cubicBez(x1234,y1234, x234,y234, x34,y34, x4,y4, tol, level+1);
|
||||||
|
} else {
|
||||||
|
glVertex2f(x4, y4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawPath(float* pts, int npts, char closed, float tol)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
glBegin(GL_LINE_STRIP);
|
||||||
|
glColor4ubv(lineColor);
|
||||||
|
glVertex2f(pts[0], pts[1]);
|
||||||
|
for (i = 0; i < npts-1; i += 3) {
|
||||||
|
float* p = &pts[i*2];
|
||||||
|
cubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7], tol, 0);
|
||||||
|
}
|
||||||
|
if (closed) {
|
||||||
|
glVertex2f(pts[0], pts[1]);
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawControlPts(float* pts, int npts)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Control lines
|
||||||
|
glColor4ubv(lineColor);
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
for (i = 0; i < npts-1; i += 3) {
|
||||||
|
float* p = &pts[i*2];
|
||||||
|
glVertex2f(p[0],p[1]);
|
||||||
|
glVertex2f(p[2],p[3]);
|
||||||
|
glVertex2f(p[4],p[5]);
|
||||||
|
glVertex2f(p[6],p[7]);
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
|
|
||||||
|
// Points
|
||||||
|
glPointSize(6.0f);
|
||||||
|
glColor4ubv(lineColor);
|
||||||
|
|
||||||
|
glBegin(GL_POINTS);
|
||||||
|
glVertex2f(pts[0],pts[1]);
|
||||||
|
for (i = 0; i < npts-1; i += 3) {
|
||||||
|
float* p = &pts[i*2];
|
||||||
|
glVertex2f(p[6],p[7]);
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
|
|
||||||
|
// Points
|
||||||
|
glPointSize(3.0f);
|
||||||
|
|
||||||
|
glBegin(GL_POINTS);
|
||||||
|
glColor4ubv(bgColor);
|
||||||
|
glVertex2f(pts[0],pts[1]);
|
||||||
|
for (i = 0; i < npts-1; i += 3) {
|
||||||
|
float* p = &pts[i*2];
|
||||||
|
glColor4ubv(lineColor);
|
||||||
|
glVertex2f(p[2],p[3]);
|
||||||
|
glVertex2f(p[4],p[5]);
|
||||||
|
glColor4ubv(bgColor);
|
||||||
|
glVertex2f(p[6],p[7]);
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawframe(GLFWwindow* window)
|
||||||
|
{
|
||||||
|
int width = 0, height = 0;
|
||||||
|
float view[4], cx, cy, hw, hh, aspect, px;
|
||||||
|
NSVGshape* shape;
|
||||||
|
NSVGpath* path;
|
||||||
|
|
||||||
|
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||||
|
glfwGetFramebufferSize(window, &width, &height);
|
||||||
|
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
glClearColor(220.0f/255.0f, 220.0f/255.0f, 220.0f/255.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
glDisable(GL_TEXTURE_2D);
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
glLoadIdentity();
|
||||||
|
|
||||||
|
// Fit view to bounds
|
||||||
|
cx = g_image->width*0.5f;
|
||||||
|
cy = g_image->height*0.5f;
|
||||||
|
hw = g_image->width*0.5f;
|
||||||
|
hh = g_image->height*0.5f;
|
||||||
|
|
||||||
|
if (width/hw < height/hh) {
|
||||||
|
aspect = (float)height / (float)width;
|
||||||
|
view[0] = cx - hw * 1.2f;
|
||||||
|
view[2] = cx + hw * 1.2f;
|
||||||
|
view[1] = cy - hw * 1.2f * aspect;
|
||||||
|
view[3] = cy + hw * 1.2f * aspect;
|
||||||
|
} else {
|
||||||
|
aspect = (float)width / (float)height;
|
||||||
|
view[0] = cx - hh * 1.2f * aspect;
|
||||||
|
view[2] = cx + hh * 1.2f * aspect;
|
||||||
|
view[1] = cy - hh * 1.2f;
|
||||||
|
view[3] = cy + hh * 1.2f;
|
||||||
|
}
|
||||||
|
// Size of one pixel.
|
||||||
|
px = (view[2] - view[1]) / (float)width;
|
||||||
|
|
||||||
|
glOrtho(view[0], view[2], view[3], view[1], -1, 1);
|
||||||
|
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glLoadIdentity();
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
glColor4ub(255,255,255,255);
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
// Draw bounds
|
||||||
|
glColor4ub(0,0,0,64);
|
||||||
|
glBegin(GL_LINE_LOOP);
|
||||||
|
glVertex2f(0, 0);
|
||||||
|
glVertex2f(g_image->width, 0);
|
||||||
|
glVertex2f(g_image->width, g_image->height);
|
||||||
|
glVertex2f(0, g_image->height);
|
||||||
|
glEnd();
|
||||||
|
|
||||||
|
for (shape = g_image->shapes; shape != NULL; shape = shape->next) {
|
||||||
|
for (path = shape->paths; path != NULL; path = path->next) {
|
||||||
|
drawPath(path->pts, path->npts, path->closed, px * 1.5f);
|
||||||
|
drawControlPts(path->pts, path->npts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwSwapBuffers(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resizecb(GLFWwindow* window, int width, int height)
|
||||||
|
{
|
||||||
|
// Update and render
|
||||||
|
NSVG_NOTUSED(width);
|
||||||
|
NSVG_NOTUSED(height);
|
||||||
|
drawframe(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
GLFWwindow* window;
|
||||||
|
const GLFWvidmode* mode;
|
||||||
|
|
||||||
|
if (!glfwInit())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||||
|
window = glfwCreateWindow(mode->width - 40, mode->height - 80, "Nano SVG", NULL, NULL);
|
||||||
|
if (!window)
|
||||||
|
{
|
||||||
|
printf("Could not open window\n");
|
||||||
|
glfwTerminate();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwSetFramebufferSizeCallback(window, resizecb);
|
||||||
|
glfwMakeContextCurrent(window);
|
||||||
|
glEnable(GL_POINT_SMOOTH);
|
||||||
|
glEnable(GL_LINE_SMOOTH);
|
||||||
|
|
||||||
|
|
||||||
|
g_image = nsvgParseFromFile("../example/nano.svg", "px", 96.0f);
|
||||||
|
if (g_image == NULL) {
|
||||||
|
printf("Could not open SVG image.\n");
|
||||||
|
glfwTerminate();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!glfwWindowShouldClose(window))
|
||||||
|
{
|
||||||
|
drawframe(window);
|
||||||
|
glfwPollEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsvgDelete(g_image);
|
||||||
|
|
||||||
|
glfwTerminate();
|
||||||
|
return 0;
|
||||||
|
}
|
69
3rd-party/nanosvg/example/example2.c
vendored
Normal file
69
3rd-party/nanosvg/example/example2.c
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013 Mikko Mononen memon@inside.org
|
||||||
|
//
|
||||||
|
// This software is provided 'as-is', without any express or implied
|
||||||
|
// warranty. In no event will the authors be held liable for any damages
|
||||||
|
// arising from the use of this software.
|
||||||
|
// Permission is granted to anyone to use this software for any purpose,
|
||||||
|
// including commercial applications, and to alter it and redistribute it
|
||||||
|
// freely, subject to the following restrictions:
|
||||||
|
// 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
// claim that you wrote the original software. If you use this software
|
||||||
|
// in a product, an acknowledgment in the product documentation would be
|
||||||
|
// appreciated but is not required.
|
||||||
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
// misrepresented as being the original software.
|
||||||
|
// 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <float.h>
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
#include "stb_image_write.h"
|
||||||
|
#define NANOSVG_IMPLEMENTATION
|
||||||
|
#include "nanosvg.h"
|
||||||
|
#define NANOSVGRAST_IMPLEMENTATION
|
||||||
|
#include "nanosvgrast.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
NSVGimage *image = NULL;
|
||||||
|
NSVGrasterizer *rast = NULL;
|
||||||
|
unsigned char* img = NULL;
|
||||||
|
int w, h;
|
||||||
|
const char* filename = "../example/23.svg";
|
||||||
|
|
||||||
|
printf("parsing %s\n", filename);
|
||||||
|
image = nsvgParseFromFile(filename, "px", 96.0f);
|
||||||
|
if (image == NULL) {
|
||||||
|
printf("Could not open SVG image.\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
w = (int)image->width;
|
||||||
|
h = (int)image->height;
|
||||||
|
|
||||||
|
rast = nsvgCreateRasterizer();
|
||||||
|
if (rast == NULL) {
|
||||||
|
printf("Could not init rasterizer.\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
img = malloc(w*h*4);
|
||||||
|
if (img == NULL) {
|
||||||
|
printf("Could not alloc image buffer.\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("rasterizing image %d x %d\n", w, h);
|
||||||
|
nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4);
|
||||||
|
|
||||||
|
printf("writing svg.png\n");
|
||||||
|
stbi_write_png("svg.png", w, h, 4, img, w*4);
|
||||||
|
|
||||||
|
error:
|
||||||
|
nsvgDeleteRasterizer(rast);
|
||||||
|
nsvgDelete(image);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
27
3rd-party/nanosvg/example/nano.svg
vendored
Normal file
27
3rd-party/nanosvg/example/nano.svg
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="640px" height="480px" viewBox="0 0 640 480" enable-background="new 0 0 640 480" xml:space="preserve">
|
||||||
|
<path d="M282.658,250.271c0,5.31-1.031,10.156-3.087,14.543c-2.059,4.387-4.984,8.152-8.774,11.293
|
||||||
|
c-3.793,3.144-8.477,5.58-14.055,7.312c-5.581,1.731-11.836,2.601-18.767,2.601c-9.968,0-18.605-1.572-25.917-4.713
|
||||||
|
s-13.299-6.986-17.955-11.536l13.812-15.111c4.116,3.684,8.584,6.499,13.405,8.449c4.819,1.95,9.993,2.925,15.518,2.925
|
||||||
|
c5.525,0,9.856-1.219,12.999-3.656c3.141-2.438,4.712-5.769,4.712-9.993c0-2.056-0.3-3.844-0.894-5.361
|
||||||
|
c-0.596-1.517-1.653-2.925-3.168-4.226c-1.518-1.3-3.549-2.519-6.093-3.655c-2.546-1.138-5.768-2.301-9.668-3.494
|
||||||
|
c-6.5-2.056-11.943-4.25-16.33-6.58c-4.387-2.328-7.937-4.9-10.643-7.719c-2.709-2.815-4.659-5.931-5.849-9.343
|
||||||
|
c-1.193-3.412-1.788-7.23-1.788-11.455c0-5.2,1.082-9.831,3.25-13.893c2.166-4.062,5.144-7.5,8.937-10.318
|
||||||
|
c3.791-2.815,8.178-4.956,13.162-6.418c4.981-1.462,10.343-2.193,16.086-2.193c8.449,0,15.842,1.247,22.179,3.737
|
||||||
|
c6.337,2.493,11.997,6.121,16.98,10.887l-12.674,14.624c-7.583-6.281-15.655-9.424-24.21-9.424c-4.875,0-8.721,0.95-11.537,2.844
|
||||||
|
c-2.818,1.896-4.225,4.578-4.225,8.043c0,1.843,0.297,3.412,0.894,4.712c0.594,1.3,1.65,2.519,3.168,3.656
|
||||||
|
c1.516,1.137,3.656,2.249,6.418,3.331c2.763,1.084,6.309,2.33,10.643,3.736c5.306,1.734,10.046,3.631,14.218,5.688
|
||||||
|
c4.169,2.06,7.662,4.524,10.48,7.394c2.815,2.871,4.981,6.174,6.5,9.911C281.898,240.603,282.658,245.071,282.658,250.271z
|
||||||
|
M335.953,260.833l20.637-90.181h27.46l-32.011,112.604h-33.634l-32.173-112.604h28.598l20.311,90.181H335.953z M437.832,286.019
|
||||||
|
c-16.357,0-28.896-5.01-37.615-15.03c-8.722-10.019-13.081-24.779-13.081-44.278c0-9.531,1.407-17.98,4.225-25.348
|
||||||
|
c2.815-7.366,6.688-13.54,11.618-18.524c4.928-4.981,10.668-8.747,17.223-11.293c6.555-2.544,13.568-3.818,21.043-3.818
|
||||||
|
c8.23,0,15.436,1.3,21.611,3.899c6.174,2.6,11.537,5.959,16.086,10.075l-14.137,14.624c-3.467-3.032-6.906-5.281-10.318-6.744
|
||||||
|
s-7.393-2.193-11.941-2.193c-4.01,0-7.693,0.731-11.051,2.193s-6.256,3.793-8.691,6.987c-2.438,3.196-4.334,7.287-5.688,12.268
|
||||||
|
c-1.355,4.984-2.031,10.996-2.031,18.037c0,7.367,0.486,13.567,1.463,18.604c0.975,5.037,2.408,9.1,4.305,12.187
|
||||||
|
c1.895,3.087,4.307,5.309,7.23,6.662c2.926,1.355,6.338,2.031,10.238,2.031c5.631,0,10.613-1.244,14.947-3.737v-25.186h-14.785
|
||||||
|
l-2.6-18.849h43.547v55.57c-5.85,3.793-12.297,6.718-19.336,8.774C453.051,284.987,445.631,286.019,437.832,286.019z M523.5,151.5
|
||||||
|
c0-6.627-5.373-12-12-12h-343c-6.627,0-12,5.373-12,12v150c0,6.627,5.373,12,12,12h343c6.627,0,12-5.373,12-12V151.5z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
BIN
3rd-party/nanosvg/example/screenshot-1.png
vendored
Normal file
BIN
3rd-party/nanosvg/example/screenshot-1.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 59 KiB |
BIN
3rd-party/nanosvg/example/screenshot-2.png
vendored
Normal file
BIN
3rd-party/nanosvg/example/screenshot-2.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 154 KiB |
511
3rd-party/nanosvg/example/stb_image_write.h
vendored
Normal file
511
3rd-party/nanosvg/example/stb_image_write.h
vendored
Normal file
|
@ -0,0 +1,511 @@
|
||||||
|
/* stbiw-0.92 - public domain - http://nothings.org/stb/stb_image_write.h
|
||||||
|
writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010
|
||||||
|
no warranty implied; use at your own risk
|
||||||
|
|
||||||
|
|
||||||
|
Before including,
|
||||||
|
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
|
||||||
|
in the file that you want to have the implementation.
|
||||||
|
|
||||||
|
|
||||||
|
ABOUT:
|
||||||
|
|
||||||
|
This header file is a library for writing images to C stdio. It could be
|
||||||
|
adapted to write to memory or a general streaming interface; let me know.
|
||||||
|
|
||||||
|
The PNG output is not optimal; it is 20-50% larger than the file
|
||||||
|
written by a decent optimizing implementation. This library is designed
|
||||||
|
for source code compactness and simplicitly, not optimal image file size
|
||||||
|
or run-time performance.
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
|
||||||
|
There are three functions, one for each image file format:
|
||||||
|
|
||||||
|
int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
|
||||||
|
int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
|
||||||
|
int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
|
||||||
|
|
||||||
|
Each function returns 0 on failure and non-0 on success.
|
||||||
|
|
||||||
|
The functions create an image file defined by the parameters. The image
|
||||||
|
is a rectangle of pixels stored from left-to-right, top-to-bottom.
|
||||||
|
Each pixel contains 'comp' channels of data stored interleaved with 8-bits
|
||||||
|
per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is
|
||||||
|
monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall.
|
||||||
|
The *data pointer points to the first byte of the top-left-most pixel.
|
||||||
|
For PNG, "stride_in_bytes" is the distance in bytes from the first byte of
|
||||||
|
a row of pixels to the first byte of the next row of pixels.
|
||||||
|
|
||||||
|
PNG creates output files with the same number of components as the input.
|
||||||
|
The BMP and TGA formats expand Y to RGB in the file format. BMP does not
|
||||||
|
output alpha.
|
||||||
|
|
||||||
|
PNG supports writing rectangles of data even when the bytes storing rows of
|
||||||
|
data are not consecutive in memory (e.g. sub-rectangles of a larger image),
|
||||||
|
by supplying the stride between the beginning of adjacent rows. The other
|
||||||
|
formats do not. (Thus you cannot write a native-format BMP through the BMP
|
||||||
|
writer, both because it is in BGR order and because it may have padding
|
||||||
|
at the end of the line.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDE_STB_IMAGE_WRITE_H
|
||||||
|
#define INCLUDE_STB_IMAGE_WRITE_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
|
||||||
|
extern int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
|
||||||
|
extern int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif//INCLUDE_STB_IMAGE_WRITE_H
|
||||||
|
|
||||||
|
#ifdef STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
typedef unsigned int stbiw_uint32;
|
||||||
|
typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];
|
||||||
|
|
||||||
|
static void writefv(FILE *f, const char *fmt, va_list v)
|
||||||
|
{
|
||||||
|
while (*fmt) {
|
||||||
|
switch (*fmt++) {
|
||||||
|
case ' ': break;
|
||||||
|
case '1': { unsigned char x = (unsigned char) va_arg(v, int); fputc(x,f); break; }
|
||||||
|
case '2': { int x = va_arg(v,int); unsigned char b[2];
|
||||||
|
b[0] = (unsigned char) x; b[1] = (unsigned char) (x>>8);
|
||||||
|
fwrite(b,2,1,f); break; }
|
||||||
|
case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4];
|
||||||
|
b[0]=(unsigned char)x; b[1]=(unsigned char)(x>>8);
|
||||||
|
b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24);
|
||||||
|
fwrite(b,4,1,f); break; }
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write3(FILE *f, unsigned char a, unsigned char b, unsigned char c)
|
||||||
|
{
|
||||||
|
unsigned char arr[3];
|
||||||
|
arr[0] = a, arr[1] = b, arr[2] = c;
|
||||||
|
fwrite(arr, 3, 1, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad)
|
||||||
|
{
|
||||||
|
unsigned char bg[3] = { 255, 0, 255}, px[3];
|
||||||
|
stbiw_uint32 zero = 0;
|
||||||
|
int i,j,k, j_end;
|
||||||
|
|
||||||
|
if (y <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (vdir < 0)
|
||||||
|
j_end = -1, j = y-1;
|
||||||
|
else
|
||||||
|
j_end = y, j = 0;
|
||||||
|
|
||||||
|
for (; j != j_end; j += vdir) {
|
||||||
|
for (i=0; i < x; ++i) {
|
||||||
|
unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
|
||||||
|
if (write_alpha < 0)
|
||||||
|
fwrite(&d[comp-1], 1, 1, f);
|
||||||
|
switch (comp) {
|
||||||
|
case 1:
|
||||||
|
case 2: write3(f, d[0],d[0],d[0]);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
if (!write_alpha) {
|
||||||
|
// composite against pink background
|
||||||
|
for (k=0; k < 3; ++k)
|
||||||
|
px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255;
|
||||||
|
write3(f, px[1-rgb_dir],px[1],px[1+rgb_dir]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
case 3:
|
||||||
|
write3(f, d[1-rgb_dir],d[1],d[1+rgb_dir]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (write_alpha > 0)
|
||||||
|
fwrite(&d[comp-1], 1, 1, f);
|
||||||
|
}
|
||||||
|
fwrite(&zero,scanline_pad,1,f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
if (y < 0 || x < 0) return 0;
|
||||||
|
f = fopen(filename, "wb");
|
||||||
|
if (f) {
|
||||||
|
va_list v;
|
||||||
|
va_start(v, fmt);
|
||||||
|
writefv(f, fmt, v);
|
||||||
|
va_end(v);
|
||||||
|
write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
return f != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
|
||||||
|
{
|
||||||
|
int pad = (-x*3) & 3;
|
||||||
|
return outfile(filename,-1,-1,x,y,comp,(void *) data,0,pad,
|
||||||
|
"11 4 22 4" "4 44 22 444444",
|
||||||
|
'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
|
||||||
|
40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
|
||||||
|
}
|
||||||
|
|
||||||
|
int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)
|
||||||
|
{
|
||||||
|
int has_alpha = !(comp & 1);
|
||||||
|
return outfile(filename, -1,-1, x, y, comp, (void *) data, has_alpha, 0,
|
||||||
|
"111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
// stretchy buffer; stbi__sbpush() == vector<>::push_back() -- stbi__sbcount() == vector<>::size()
|
||||||
|
#define stbi__sbraw(a) ((int *) (a) - 2)
|
||||||
|
#define stbi__sbm(a) stbi__sbraw(a)[0]
|
||||||
|
#define stbi__sbn(a) stbi__sbraw(a)[1]
|
||||||
|
|
||||||
|
#define stbi__sbneedgrow(a,n) ((a)==0 || stbi__sbn(a)+n >= stbi__sbm(a))
|
||||||
|
#define stbi__sbmaybegrow(a,n) (stbi__sbneedgrow(a,(n)) ? stbi__sbgrow(a,n) : 0)
|
||||||
|
#define stbi__sbgrow(a,n) stbi__sbgrowf((void **) &(a), (n), sizeof(*(a)))
|
||||||
|
|
||||||
|
#define stbi__sbpush(a, v) (stbi__sbmaybegrow(a,1), (a)[stbi__sbn(a)++] = (v))
|
||||||
|
#define stbi__sbcount(a) ((a) ? stbi__sbn(a) : 0)
|
||||||
|
#define stbi__sbfree(a) ((a) ? free(stbi__sbraw(a)),0 : 0)
|
||||||
|
|
||||||
|
static void *stbi__sbgrowf(void **arr, int increment, int itemsize)
|
||||||
|
{
|
||||||
|
int m = *arr ? 2*stbi__sbm(*arr)+increment : increment+1;
|
||||||
|
void *p = realloc(*arr ? stbi__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2);
|
||||||
|
assert(p);
|
||||||
|
if (p) {
|
||||||
|
if (!*arr) ((int *) p)[1] = 0;
|
||||||
|
*arr = (void *) ((int *) p + 2);
|
||||||
|
stbi__sbm(*arr) = m;
|
||||||
|
}
|
||||||
|
return *arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char *stbi__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
|
||||||
|
{
|
||||||
|
while (*bitcount >= 8) {
|
||||||
|
stbi__sbpush(data, (unsigned char) *bitbuffer);
|
||||||
|
*bitbuffer >>= 8;
|
||||||
|
*bitcount -= 8;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stbi__zlib_bitrev(int code, int codebits)
|
||||||
|
{
|
||||||
|
int res=0;
|
||||||
|
while (codebits--) {
|
||||||
|
res = (res << 1) | (code & 1);
|
||||||
|
code >>= 1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int stbi__zlib_countm(unsigned char *a, unsigned char *b, int limit)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i < limit && i < 258; ++i)
|
||||||
|
if (a[i] != b[i]) break;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int stbi__zhash(unsigned char *data)
|
||||||
|
{
|
||||||
|
stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);
|
||||||
|
hash ^= hash << 3;
|
||||||
|
hash += hash >> 5;
|
||||||
|
hash ^= hash << 4;
|
||||||
|
hash += hash >> 17;
|
||||||
|
hash ^= hash << 25;
|
||||||
|
hash += hash >> 6;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define stbi__zlib_flush() (out = stbi__zlib_flushf(out, &bitbuf, &bitcount))
|
||||||
|
#define stbi__zlib_add(code,codebits) \
|
||||||
|
(bitbuf |= (code) << bitcount, bitcount += (codebits), stbi__zlib_flush())
|
||||||
|
#define stbi__zlib_huffa(b,c) stbi__zlib_add(stbi__zlib_bitrev(b,c),c)
|
||||||
|
// default huffman tables
|
||||||
|
#define stbi__zlib_huff1(n) stbi__zlib_huffa(0x30 + (n), 8)
|
||||||
|
#define stbi__zlib_huff2(n) stbi__zlib_huffa(0x190 + (n)-144, 9)
|
||||||
|
#define stbi__zlib_huff3(n) stbi__zlib_huffa(0 + (n)-256,7)
|
||||||
|
#define stbi__zlib_huff4(n) stbi__zlib_huffa(0xc0 + (n)-280,8)
|
||||||
|
#define stbi__zlib_huff(n) ((n) <= 143 ? stbi__zlib_huff1(n) : (n) <= 255 ? stbi__zlib_huff2(n) : (n) <= 279 ? stbi__zlib_huff3(n) : stbi__zlib_huff4(n))
|
||||||
|
#define stbi__zlib_huffb(n) ((n) <= 143 ? stbi__zlib_huff1(n) : stbi__zlib_huff2(n))
|
||||||
|
|
||||||
|
#define stbi__ZHASH 16384
|
||||||
|
|
||||||
|
unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)
|
||||||
|
{
|
||||||
|
static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 };
|
||||||
|
static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 };
|
||||||
|
static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 };
|
||||||
|
static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };
|
||||||
|
unsigned int bitbuf=0;
|
||||||
|
int i,j, bitcount=0;
|
||||||
|
unsigned char *out = NULL;
|
||||||
|
unsigned char **hash_table[stbi__ZHASH]; // 64KB on the stack!
|
||||||
|
if (quality < 5) quality = 5;
|
||||||
|
|
||||||
|
stbi__sbpush(out, 0x78); // DEFLATE 32K window
|
||||||
|
stbi__sbpush(out, 0x5e); // FLEVEL = 1
|
||||||
|
stbi__zlib_add(1,1); // BFINAL = 1
|
||||||
|
stbi__zlib_add(1,2); // BTYPE = 1 -- fixed huffman
|
||||||
|
|
||||||
|
for (i=0; i < stbi__ZHASH; ++i)
|
||||||
|
hash_table[i] = NULL;
|
||||||
|
|
||||||
|
i=0;
|
||||||
|
while (i < data_len-3) {
|
||||||
|
// hash next 3 bytes of data to be compressed
|
||||||
|
int h = stbi__zhash(data+i)&(stbi__ZHASH-1), best=3;
|
||||||
|
unsigned char *bestloc = 0;
|
||||||
|
unsigned char **hlist = hash_table[h];
|
||||||
|
int n = stbi__sbcount(hlist);
|
||||||
|
for (j=0; j < n; ++j) {
|
||||||
|
if (hlist[j]-data > i-32768) { // if entry lies within window
|
||||||
|
int d = stbi__zlib_countm(hlist[j], data+i, data_len-i);
|
||||||
|
if (d >= best) best=d,bestloc=hlist[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// when hash table entry is too long, delete half the entries
|
||||||
|
if (hash_table[h] && stbi__sbn(hash_table[h]) == 2*quality) {
|
||||||
|
memcpy(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);
|
||||||
|
stbi__sbn(hash_table[h]) = quality;
|
||||||
|
}
|
||||||
|
stbi__sbpush(hash_table[h],data+i);
|
||||||
|
|
||||||
|
if (bestloc) {
|
||||||
|
// "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal
|
||||||
|
h = stbi__zhash(data+i+1)&(stbi__ZHASH-1);
|
||||||
|
hlist = hash_table[h];
|
||||||
|
n = stbi__sbcount(hlist);
|
||||||
|
for (j=0; j < n; ++j) {
|
||||||
|
if (hlist[j]-data > i-32767) {
|
||||||
|
int e = stbi__zlib_countm(hlist[j], data+i+1, data_len-i-1);
|
||||||
|
if (e > best) { // if next match is better, bail on current match
|
||||||
|
bestloc = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestloc) {
|
||||||
|
int d = data+i - bestloc; // distance back
|
||||||
|
assert(d <= 32767 && best <= 258);
|
||||||
|
for (j=0; best > lengthc[j+1]-1; ++j);
|
||||||
|
stbi__zlib_huff(j+257);
|
||||||
|
if (lengtheb[j]) stbi__zlib_add(best - lengthc[j], lengtheb[j]);
|
||||||
|
for (j=0; d > distc[j+1]-1; ++j);
|
||||||
|
stbi__zlib_add(stbi__zlib_bitrev(j,5),5);
|
||||||
|
if (disteb[j]) stbi__zlib_add(d - distc[j], disteb[j]);
|
||||||
|
i += best;
|
||||||
|
} else {
|
||||||
|
stbi__zlib_huffb(data[i]);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// write out final bytes
|
||||||
|
for (;i < data_len; ++i)
|
||||||
|
stbi__zlib_huffb(data[i]);
|
||||||
|
stbi__zlib_huff(256); // end of block
|
||||||
|
// pad with 0 bits to byte boundary
|
||||||
|
while (bitcount)
|
||||||
|
stbi__zlib_add(0,1);
|
||||||
|
|
||||||
|
for (i=0; i < stbi__ZHASH; ++i)
|
||||||
|
(void) stbi__sbfree(hash_table[i]);
|
||||||
|
|
||||||
|
{
|
||||||
|
// compute adler32 on input
|
||||||
|
unsigned int i=0, s1=1, s2=0, blocklen = data_len % 5552;
|
||||||
|
int j=0;
|
||||||
|
while (j < data_len) {
|
||||||
|
for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1;
|
||||||
|
s1 %= 65521, s2 %= 65521;
|
||||||
|
j += blocklen;
|
||||||
|
blocklen = 5552;
|
||||||
|
}
|
||||||
|
stbi__sbpush(out, (unsigned char) (s2 >> 8));
|
||||||
|
stbi__sbpush(out, (unsigned char) s2);
|
||||||
|
stbi__sbpush(out, (unsigned char) (s1 >> 8));
|
||||||
|
stbi__sbpush(out, (unsigned char) s1);
|
||||||
|
}
|
||||||
|
*out_len = stbi__sbn(out);
|
||||||
|
// make returned pointer freeable
|
||||||
|
memmove(stbi__sbraw(out), out, *out_len);
|
||||||
|
return (unsigned char *) stbi__sbraw(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int stbi__crc32(unsigned char *buffer, int len)
|
||||||
|
{
|
||||||
|
static unsigned int crc_table[256];
|
||||||
|
unsigned int crc = ~0u;
|
||||||
|
int i,j;
|
||||||
|
if (crc_table[1] == 0)
|
||||||
|
for(i=0; i < 256; i++)
|
||||||
|
for (crc_table[i]=i, j=0; j < 8; ++j)
|
||||||
|
crc_table[i] = (crc_table[i] >> 1) ^ (crc_table[i] & 1 ? 0xedb88320 : 0);
|
||||||
|
for (i=0; i < len; ++i)
|
||||||
|
crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
|
||||||
|
return ~crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define stbi__wpng4(o,a,b,c,d) ((o)[0]=(unsigned char)(a),(o)[1]=(unsigned char)(b),(o)[2]=(unsigned char)(c),(o)[3]=(unsigned char)(d),(o)+=4)
|
||||||
|
#define stbi__wp32(data,v) stbi__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v));
|
||||||
|
#define stbi__wptag(data,s) stbi__wpng4(data, s[0],s[1],s[2],s[3])
|
||||||
|
|
||||||
|
static void stbi__wpcrc(unsigned char **data, int len)
|
||||||
|
{
|
||||||
|
unsigned int crc = stbi__crc32(*data - len - 4, len+4);
|
||||||
|
stbi__wp32(*data, crc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char stbi__paeth(int a, int b, int c)
|
||||||
|
{
|
||||||
|
int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c);
|
||||||
|
if (pa <= pb && pa <= pc) return (unsigned char) a;
|
||||||
|
if (pb <= pc) return (unsigned char) b;
|
||||||
|
return (unsigned char) c;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)
|
||||||
|
{
|
||||||
|
int ctype[5] = { -1, 0, 4, 2, 6 };
|
||||||
|
unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };
|
||||||
|
unsigned char *out,*o, *filt, *zlib;
|
||||||
|
signed char *line_buffer;
|
||||||
|
int i,j,k,p,zlen;
|
||||||
|
|
||||||
|
if (stride_bytes == 0)
|
||||||
|
stride_bytes = x * n;
|
||||||
|
|
||||||
|
filt = (unsigned char *) malloc((x*n+1) * y); if (!filt) return 0;
|
||||||
|
line_buffer = (signed char *) malloc(x * n); if (!line_buffer) { free(filt); return 0; }
|
||||||
|
for (j=0; j < y; ++j) {
|
||||||
|
static int mapping[] = { 0,1,2,3,4 };
|
||||||
|
static int firstmap[] = { 0,1,0,5,6 };
|
||||||
|
int *mymap = j ? mapping : firstmap;
|
||||||
|
int best = 0, bestval = 0x7fffffff;
|
||||||
|
for (p=0; p < 2; ++p) {
|
||||||
|
for (k= p?best:0; k < 5; ++k) {
|
||||||
|
int type = mymap[k],est=0;
|
||||||
|
unsigned char *z = pixels + stride_bytes*j;
|
||||||
|
for (i=0; i < n; ++i)
|
||||||
|
switch (type) {
|
||||||
|
case 0: line_buffer[i] = z[i]; break;
|
||||||
|
case 1: line_buffer[i] = z[i]; break;
|
||||||
|
case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break;
|
||||||
|
case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break;
|
||||||
|
case 4: line_buffer[i] = (signed char) (z[i] - stbi__paeth(0,z[i-stride_bytes],0)); break;
|
||||||
|
case 5: line_buffer[i] = z[i]; break;
|
||||||
|
case 6: line_buffer[i] = z[i]; break;
|
||||||
|
}
|
||||||
|
for (i=n; i < x*n; ++i) {
|
||||||
|
switch (type) {
|
||||||
|
case 0: line_buffer[i] = z[i]; break;
|
||||||
|
case 1: line_buffer[i] = z[i] - z[i-n]; break;
|
||||||
|
case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break;
|
||||||
|
case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break;
|
||||||
|
case 4: line_buffer[i] = z[i] - stbi__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break;
|
||||||
|
case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break;
|
||||||
|
case 6: line_buffer[i] = z[i] - stbi__paeth(z[i-n], 0,0); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p) break;
|
||||||
|
for (i=0; i < x*n; ++i)
|
||||||
|
est += abs((signed char) line_buffer[i]);
|
||||||
|
if (est < bestval) { bestval = est; best = k; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// when we get here, best contains the filter type, and line_buffer contains the data
|
||||||
|
filt[j*(x*n+1)] = (unsigned char) best;
|
||||||
|
memcpy(filt+j*(x*n+1)+1, line_buffer, x*n);
|
||||||
|
}
|
||||||
|
free(line_buffer);
|
||||||
|
zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory
|
||||||
|
free(filt);
|
||||||
|
if (!zlib) return 0;
|
||||||
|
|
||||||
|
// each tag requires 12 bytes of overhead
|
||||||
|
out = (unsigned char *) malloc(8 + 12+13 + 12+zlen + 12);
|
||||||
|
if (!out) return 0;
|
||||||
|
*out_len = 8 + 12+13 + 12+zlen + 12;
|
||||||
|
|
||||||
|
o=out;
|
||||||
|
memcpy(o,sig,8); o+= 8;
|
||||||
|
stbi__wp32(o, 13); // header length
|
||||||
|
stbi__wptag(o, "IHDR");
|
||||||
|
stbi__wp32(o, x);
|
||||||
|
stbi__wp32(o, y);
|
||||||
|
*o++ = 8;
|
||||||
|
*o++ = (unsigned char) ctype[n];
|
||||||
|
*o++ = 0;
|
||||||
|
*o++ = 0;
|
||||||
|
*o++ = 0;
|
||||||
|
stbi__wpcrc(&o,13);
|
||||||
|
|
||||||
|
stbi__wp32(o, zlen);
|
||||||
|
stbi__wptag(o, "IDAT");
|
||||||
|
memcpy(o, zlib, zlen); o += zlen; free(zlib);
|
||||||
|
stbi__wpcrc(&o, zlen);
|
||||||
|
|
||||||
|
stbi__wp32(o,0);
|
||||||
|
stbi__wptag(o, "IEND");
|
||||||
|
stbi__wpcrc(&o,0);
|
||||||
|
|
||||||
|
assert(o == out + *out_len);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
int len;
|
||||||
|
unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);
|
||||||
|
if (!png) return 0;
|
||||||
|
f = fopen(filename, "wb");
|
||||||
|
if (!f) { free(png); return 0; }
|
||||||
|
fwrite(png, 1, len, f);
|
||||||
|
fclose(f);
|
||||||
|
free(png);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif // STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
|
||||||
|
/* Revision history
|
||||||
|
|
||||||
|
0.92 (2010-08-01)
|
||||||
|
casts to unsigned char to fix warnings
|
||||||
|
0.91 (2010-07-17)
|
||||||
|
first public release
|
||||||
|
0.90 first internal release
|
||||||
|
*/
|
56
3rd-party/nanosvg/premake4.lua
vendored
Normal file
56
3rd-party/nanosvg/premake4.lua
vendored
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
|
||||||
|
local action = _ACTION or ""
|
||||||
|
|
||||||
|
solution "nanosvg"
|
||||||
|
location ( "build" )
|
||||||
|
configurations { "Debug", "Release" }
|
||||||
|
platforms {"native", "x64", "x32"}
|
||||||
|
|
||||||
|
project "example1"
|
||||||
|
kind "ConsoleApp"
|
||||||
|
language "C++"
|
||||||
|
files { "example/example1.c", "example/*.h", "src/*.h" }
|
||||||
|
includedirs { "example", "src" }
|
||||||
|
targetdir("build")
|
||||||
|
|
||||||
|
configuration { "linux" }
|
||||||
|
links { "X11","Xrandr", "rt", "GL", "GLU", "pthread", "glfw" }
|
||||||
|
|
||||||
|
configuration { "windows" }
|
||||||
|
links { "glu32","opengl32", "gdi32", "winmm", "user32" }
|
||||||
|
|
||||||
|
configuration { "macosx" }
|
||||||
|
links { "glfw3" }
|
||||||
|
linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo" }
|
||||||
|
|
||||||
|
configuration "Debug"
|
||||||
|
defines { "DEBUG" }
|
||||||
|
flags { "Symbols", "ExtraWarnings"}
|
||||||
|
|
||||||
|
configuration "Release"
|
||||||
|
defines { "NDEBUG" }
|
||||||
|
flags { "Optimize", "ExtraWarnings"}
|
||||||
|
|
||||||
|
project "example2"
|
||||||
|
kind "ConsoleApp"
|
||||||
|
language "C++"
|
||||||
|
files { "example/example2.c", "example/*.h", "src/*.h" }
|
||||||
|
includedirs { "example", "src" }
|
||||||
|
targetdir("build")
|
||||||
|
|
||||||
|
configuration { "linux" }
|
||||||
|
links { "X11","Xrandr", "rt", "pthread" }
|
||||||
|
|
||||||
|
configuration { "windows" }
|
||||||
|
links { "winmm", "user32" }
|
||||||
|
|
||||||
|
configuration { "macosx" }
|
||||||
|
linkoptions { "-framework Cocoa", "-framework IOKit" }
|
||||||
|
|
||||||
|
configuration "Debug"
|
||||||
|
defines { "DEBUG" }
|
||||||
|
flags { "Symbols", "ExtraWarnings"}
|
||||||
|
|
||||||
|
configuration "Release"
|
||||||
|
defines { "NDEBUG" }
|
||||||
|
flags { "Optimize", "ExtraWarnings"}
|
3098
3rd-party/nanosvg/src/nanosvg.h
vendored
Normal file
3098
3rd-party/nanosvg/src/nanosvg.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
1458
3rd-party/nanosvg/src/nanosvgrast.h
vendored
Normal file
1458
3rd-party/nanosvg/src/nanosvgrast.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
PKGBUILD
1
PKGBUILD
|
@ -13,6 +13,7 @@ depends=(
|
||||||
'pixman'
|
'pixman'
|
||||||
'libyaml'
|
'libyaml'
|
||||||
'alsa-lib'
|
'alsa-lib'
|
||||||
|
'libpng'
|
||||||
'libudev.so'
|
'libudev.so'
|
||||||
'json-c'
|
'json-c'
|
||||||
'libmpdclient'
|
'libmpdclient'
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "bar.h"
|
#include "bar.h"
|
||||||
|
#include "icon.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
|
@ -9,6 +9,8 @@ struct private
|
||||||
char *monitor;
|
char *monitor;
|
||||||
enum bar_layer layer;
|
enum bar_layer layer;
|
||||||
enum bar_location location;
|
enum bar_location location;
|
||||||
|
struct basedirs *basedirs;
|
||||||
|
struct themes *themes;
|
||||||
int height;
|
int height;
|
||||||
int left_spacing, right_spacing;
|
int left_spacing, right_spacing;
|
||||||
int left_margin, right_margin;
|
int left_margin, right_margin;
|
||||||
|
|
|
@ -240,6 +240,24 @@ conf_verify_font_shaping(keychain_t *chain, const struct yml_node *node)
|
||||||
return conf_verify_enum(chain, node, (const char *[]){"full", /*"graphemes",*/ "none"}, 2);
|
return conf_verify_enum(chain, node, (const char *[]){"full", /*"graphemes",*/ "none"}, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
conf_verify_string_list(keychain_t *chain, const struct yml_node *node)
|
||||||
|
{
|
||||||
|
if (!yml_is_list(node)) {
|
||||||
|
LOG_ERR("%s: must be a list of strings", conf_err_prefix(chain, node));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it)) {
|
||||||
|
if (!yml_is_scalar(it.node)) {
|
||||||
|
LOG_ERR("%s: must be a string", conf_err_prefix(chain, node));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
conf_verify_decoration(keychain_t *chain, const struct yml_node *node)
|
conf_verify_decoration(keychain_t *chain, const struct yml_node *node)
|
||||||
{
|
{
|
||||||
|
@ -451,6 +469,12 @@ conf_verify_bar(const struct yml_node *bar)
|
||||||
{"font-shaping", false, &conf_verify_font_shaping},
|
{"font-shaping", false, &conf_verify_font_shaping},
|
||||||
{"foreground", false, &conf_verify_color},
|
{"foreground", false, &conf_verify_color},
|
||||||
|
|
||||||
|
{"icon-basedirs", false, &conf_verify_string_list},
|
||||||
|
// TODO verify icon name
|
||||||
|
//
|
||||||
|
{"icon-themes", false, &conf_verify_string_list},
|
||||||
|
{"icon-size", false, &conf_verify_unsigned},
|
||||||
|
|
||||||
{"left", false, &verify_module_list},
|
{"left", false, &verify_module_list},
|
||||||
{"center", false, &verify_module_list},
|
{"center", false, &verify_module_list},
|
||||||
{"right", false, &verify_module_list},
|
{"right", false, &verify_module_list},
|
||||||
|
|
|
@ -48,3 +48,5 @@ bool conf_verify_particle(keychain_t *chain, const struct yml_node *node);
|
||||||
bool conf_verify_particle_list_items(keychain_t *chain, const struct yml_node *node);
|
bool conf_verify_particle_list_items(keychain_t *chain, const struct yml_node *node);
|
||||||
|
|
||||||
bool conf_verify_decoration(keychain_t *chain, const struct yml_node *node);
|
bool conf_verify_decoration(keychain_t *chain, const struct yml_node *node);
|
||||||
|
|
||||||
|
bool conf_verify_string_list(keychain_t *chain, const struct yml_node *node);
|
||||||
|
|
146
config.c
146
config.c
|
@ -5,14 +5,18 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <wordexp.h>
|
||||||
|
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
||||||
#include "bar/bar.h"
|
#include "bar/bar.h"
|
||||||
#include "color.h"
|
#include "color.h"
|
||||||
#include "config-verify.h"
|
#include "config-verify.h"
|
||||||
|
#include "icon.h"
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
|
#include "tllist.h"
|
||||||
|
#include "yml.h"
|
||||||
|
|
||||||
#define LOG_MODULE "config"
|
#define LOG_MODULE "config"
|
||||||
#define LOG_ENABLE_DBG 0
|
#define LOG_ENABLE_DBG 0
|
||||||
|
@ -130,6 +134,42 @@ conf_to_font_shaping(const struct yml_node *node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
conf_to_themes(const struct yml_node *node, struct basedirs **basedirs, struct themes **themes)
|
||||||
|
{
|
||||||
|
*basedirs = basedirs_new();
|
||||||
|
|
||||||
|
size_t idx = 0;
|
||||||
|
for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it), idx++) {
|
||||||
|
const char *s = yml_value_as_string(it.node);
|
||||||
|
wordexp_t p;
|
||||||
|
if (wordexp(s, &p, WRDE_UNDEF) == 0) {
|
||||||
|
if (dir_exists(p.we_wordv[0])) {
|
||||||
|
tll_push_back((*basedirs)->basedirs, strdup(p.we_wordv[0]));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
wordfree(&p);
|
||||||
|
}
|
||||||
|
LOG_WARN("%s is not a directory", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tll_length((*basedirs)->basedirs) == 0) {
|
||||||
|
LOG_WARN("No valid base directories provided, no themes initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
*themes = init_themes(*basedirs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
conf_to_string_list(const struct yml_node *node, struct string_list **string_list)
|
||||||
|
{
|
||||||
|
*string_list = string_list_new();
|
||||||
|
size_t idx = 0;
|
||||||
|
for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it), idx++) {
|
||||||
|
tll_push_back((*string_list)->strings, strdup(yml_value_as_string(it.node)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct deco *
|
struct deco *
|
||||||
conf_to_deco(const struct yml_node *node)
|
conf_to_deco(const struct yml_node *node)
|
||||||
{
|
{
|
||||||
|
@ -167,8 +207,10 @@ particle_simple_list_from_config(const struct yml_node *node, struct conf_inheri
|
||||||
assert(particle_list_new != NULL);
|
assert(particle_list_new != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct particle *common = particle_common_new(0, 0, NULL, fcft_clone(inherited.font), inherited.font_shaping,
|
struct particle *common
|
||||||
inherited.foreground, NULL);
|
= particle_common_new(0, 0, NULL, fcft_clone(inherited.font), inherited.font_shaping,
|
||||||
|
basedirs_inc(inherited.basedirs), themes_inc(inherited.themes),
|
||||||
|
string_list_inc(inherited.icon_themes), inherited.icon_size, inherited.foreground, NULL);
|
||||||
|
|
||||||
return particle_list_new(common, parts, count, 0, 2);
|
return particle_list_new(common, parts, count, 0, 2);
|
||||||
}
|
}
|
||||||
|
@ -190,6 +232,9 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited)
|
||||||
const struct yml_node *font_shaping_node = yml_get_value(pair.value, "font-shaping");
|
const struct yml_node *font_shaping_node = yml_get_value(pair.value, "font-shaping");
|
||||||
const struct yml_node *foreground_node = yml_get_value(pair.value, "foreground");
|
const struct yml_node *foreground_node = yml_get_value(pair.value, "foreground");
|
||||||
const struct yml_node *deco_node = yml_get_value(pair.value, "deco");
|
const struct yml_node *deco_node = yml_get_value(pair.value, "deco");
|
||||||
|
const struct yml_node *basedirs_node = yml_get_value(pair.value, "icon-basedirs");
|
||||||
|
const struct yml_node *icon_themes_node = yml_get_value(pair.value, "icon-themes");
|
||||||
|
const struct yml_node *icon_size_node = yml_get_value(pair.value, "icon-size");
|
||||||
|
|
||||||
int left = margin != NULL ? yml_value_as_int(margin) : left_margin != NULL ? yml_value_as_int(left_margin) : 0;
|
int left = margin != NULL ? yml_value_as_int(margin) : left_margin != NULL ? yml_value_as_int(left_margin) : 0;
|
||||||
int right = margin != NULL ? yml_value_as_int(margin) : right_margin != NULL ? yml_value_as_int(right_margin) : 0;
|
int right = margin != NULL ? yml_value_as_int(margin) : right_margin != NULL ? yml_value_as_int(right_margin) : 0;
|
||||||
|
@ -271,9 +316,27 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited)
|
||||||
= font_shaping_node != NULL ? conf_to_font_shaping(font_shaping_node) : inherited.font_shaping;
|
= font_shaping_node != NULL ? conf_to_font_shaping(font_shaping_node) : inherited.font_shaping;
|
||||||
pixman_color_t foreground = foreground_node != NULL ? conf_to_color(foreground_node) : inherited.foreground;
|
pixman_color_t foreground = foreground_node != NULL ? conf_to_color(foreground_node) : inherited.foreground;
|
||||||
|
|
||||||
|
struct themes *themes = NULL;
|
||||||
|
struct basedirs *basedirs = NULL;
|
||||||
|
if (basedirs_node) {
|
||||||
|
conf_to_themes(basedirs_node, &basedirs, &themes);
|
||||||
|
} else {
|
||||||
|
basedirs = basedirs_inc(inherited.basedirs);
|
||||||
|
themes = themes_inc(inherited.themes);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct string_list *icon_themes = NULL;
|
||||||
|
if (icon_themes_node) {
|
||||||
|
conf_to_string_list(icon_themes_node, &icon_themes);
|
||||||
|
} else {
|
||||||
|
icon_themes = string_list_inc(inherited.icon_themes);
|
||||||
|
}
|
||||||
|
|
||||||
|
int icon_size = icon_size_node != NULL ? yml_value_as_int(icon_size_node) : inherited.icon_size;
|
||||||
|
|
||||||
/* Instantiate base/common particle */
|
/* Instantiate base/common particle */
|
||||||
struct particle *common
|
struct particle *common = particle_common_new(left, right, on_click_templates, font, font_shaping, basedirs, themes,
|
||||||
= particle_common_new(left, right, on_click_templates, font, font_shaping, foreground, deco);
|
icon_themes, icon_size, foreground, deco);
|
||||||
|
|
||||||
const struct particle_iface *iface = plugin_load_particle(type);
|
const struct particle_iface *iface = plugin_load_particle(type);
|
||||||
|
|
||||||
|
@ -421,15 +484,40 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend)
|
||||||
if (font_shaping_node != NULL)
|
if (font_shaping_node != NULL)
|
||||||
font_shaping = conf_to_font_shaping(font_shaping_node);
|
font_shaping = conf_to_font_shaping(font_shaping_node);
|
||||||
|
|
||||||
|
// Get a default icon basedirs
|
||||||
|
struct basedirs *basedirs = NULL;
|
||||||
|
struct themes *themes = NULL;
|
||||||
|
const struct yml_node *basedirs_node = yml_get_value(bar, "icon-basedirs");
|
||||||
|
if (basedirs_node != NULL) {
|
||||||
|
conf_to_themes(basedirs_node, &basedirs, &themes);
|
||||||
|
} else {
|
||||||
|
basedirs = get_basedirs();
|
||||||
|
themes = init_themes(basedirs);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct yml_node *icon_themes_node = yml_get_value(bar, "icon-themes");
|
||||||
|
struct string_list *icon_themes = NULL;
|
||||||
|
if (icon_themes_node != NULL) {
|
||||||
|
conf_to_string_list(icon_themes_node, &icon_themes);
|
||||||
|
} else {
|
||||||
|
// Just use an empty list by default.
|
||||||
|
icon_themes = string_list_new();
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct yml_node *icon_size_node = yml_get_value(bar, "icon-size");
|
||||||
|
int icon_size = icon_size_node ? yml_value_as_int(icon_size_node) : conf.height;
|
||||||
|
|
||||||
const struct yml_node *foreground_node = yml_get_value(bar, "foreground");
|
const struct yml_node *foreground_node = yml_get_value(bar, "foreground");
|
||||||
if (foreground_node != NULL)
|
if (foreground_node != NULL)
|
||||||
foreground = conf_to_color(foreground_node);
|
foreground = conf_to_color(foreground_node);
|
||||||
|
|
||||||
struct conf_inherit inherited = {
|
struct conf_inherit inherited = {.font = font,
|
||||||
.font = font,
|
.font_shaping = font_shaping,
|
||||||
.font_shaping = font_shaping,
|
.basedirs = basedirs,
|
||||||
.foreground = foreground,
|
.themes = themes,
|
||||||
};
|
.icon_themes = icon_themes,
|
||||||
|
.icon_size = icon_size,
|
||||||
|
.foreground = foreground};
|
||||||
|
|
||||||
const struct yml_node *left = yml_get_value(bar, "left");
|
const struct yml_node *left = yml_get_value(bar, "left");
|
||||||
const struct yml_node *center = yml_get_value(bar, "center");
|
const struct yml_node *center = yml_get_value(bar, "center");
|
||||||
|
@ -456,16 +544,41 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend)
|
||||||
const struct yml_node *mod_font = yml_get_value(m.value, "font");
|
const struct yml_node *mod_font = yml_get_value(m.value, "font");
|
||||||
const struct yml_node *mod_font_shaping = yml_get_value(m.value, "font-shaping");
|
const struct yml_node *mod_font_shaping = yml_get_value(m.value, "font-shaping");
|
||||||
const struct yml_node *mod_foreground = yml_get_value(m.value, "foreground");
|
const struct yml_node *mod_foreground = yml_get_value(m.value, "foreground");
|
||||||
|
const struct yml_node *mod_basedirs = yml_get_value(m.value, "icon-basedirs");
|
||||||
|
const struct yml_node *mod_icon_themes = yml_get_value(m.value, "icon-themes");
|
||||||
|
const struct yml_node *mod_icon_size = yml_get_value(m.value, "icon-size");
|
||||||
|
|
||||||
struct conf_inherit mod_inherit = {
|
struct basedirs *inherit_basedirs = NULL;
|
||||||
.font = mod_font != NULL ? conf_to_font(mod_font) : inherited.font,
|
struct themes *inherit_themes = NULL;
|
||||||
.font_shaping
|
if (mod_basedirs) {
|
||||||
= mod_font_shaping != NULL ? conf_to_font_shaping(mod_font_shaping) : inherited.font_shaping,
|
conf_to_themes(mod_basedirs, &inherit_basedirs, &inherit_themes);
|
||||||
.foreground = mod_foreground != NULL ? conf_to_color(mod_foreground) : inherited.foreground,
|
} else {
|
||||||
};
|
inherit_themes = themes_inc(inherited.themes);
|
||||||
|
inherit_basedirs = basedirs_inc(inherited.basedirs);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct string_list *inherit_icon_themes = NULL;
|
||||||
|
if (mod_icon_themes) {
|
||||||
|
conf_to_string_list(mod_icon_themes, &inherit_icon_themes);
|
||||||
|
} else {
|
||||||
|
inherit_icon_themes = string_list_inc(inherited.icon_themes);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct conf_inherit mod_inherit
|
||||||
|
= {.font = mod_font != NULL ? conf_to_font(mod_font) : inherited.font,
|
||||||
|
.font_shaping
|
||||||
|
= mod_font_shaping != NULL ? conf_to_font_shaping(mod_font_shaping) : inherited.font_shaping,
|
||||||
|
.basedirs = inherit_basedirs,
|
||||||
|
.themes = inherit_themes,
|
||||||
|
.icon_themes = inherit_icon_themes,
|
||||||
|
.icon_size = mod_icon_size != NULL ? yml_value_as_int(mod_icon_size) : inherited.icon_size,
|
||||||
|
.foreground = mod_foreground != NULL ? conf_to_color(mod_foreground) : inherited.foreground};
|
||||||
|
|
||||||
const struct module_iface *iface = plugin_load_module(mod_name);
|
const struct module_iface *iface = plugin_load_module(mod_name);
|
||||||
mods[idx] = iface->from_conf(m.value, mod_inherit);
|
mods[idx] = iface->from_conf(m.value, mod_inherit);
|
||||||
|
themes_dec(inherit_themes);
|
||||||
|
basedirs_dec(inherit_basedirs);
|
||||||
|
string_list_dec(inherit_icon_themes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
|
@ -487,6 +600,9 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend)
|
||||||
free(conf.center.mods);
|
free(conf.center.mods);
|
||||||
free(conf.right.mods);
|
free(conf.right.mods);
|
||||||
fcft_destroy(font);
|
fcft_destroy(font);
|
||||||
|
themes_dec(themes);
|
||||||
|
basedirs_dec(basedirs);
|
||||||
|
string_list_dec(icon_themes);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
5
config.h
5
config.h
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "bar/bar.h"
|
#include "bar/bar.h"
|
||||||
#include "font-shaping.h"
|
#include "font-shaping.h"
|
||||||
|
#include "icon.h"
|
||||||
#include "yml.h"
|
#include "yml.h"
|
||||||
#include <fcft/fcft.h>
|
#include <fcft/fcft.h>
|
||||||
|
|
||||||
|
@ -22,6 +23,10 @@ enum font_shaping conf_to_font_shaping(const struct yml_node *node);
|
||||||
struct conf_inherit {
|
struct conf_inherit {
|
||||||
const struct fcft_font *font;
|
const struct fcft_font *font;
|
||||||
enum font_shaping font_shaping;
|
enum font_shaping font_shaping;
|
||||||
|
struct themes *themes;
|
||||||
|
struct basedirs *basedirs;
|
||||||
|
struct string_list *icon_themes;
|
||||||
|
int icon_size;
|
||||||
pixman_color_t foreground;
|
pixman_color_t foreground;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
968
icon.c
Normal file
968
icon.c
Normal file
|
@ -0,0 +1,968 @@
|
||||||
|
#include "tllist.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <nanosvg/nanosvg.h>
|
||||||
|
#include <nanosvg/nanosvgrast.h>
|
||||||
|
#include <pixman.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wordexp.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "icon"
|
||||||
|
#define LOG_ENABLE_DBG 0
|
||||||
|
#include "icon.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "png-yambar.h"
|
||||||
|
#include "stride.h"
|
||||||
|
#include "stringop.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
icon_pixmaps_free(const struct ref *ref)
|
||||||
|
{
|
||||||
|
struct icon_pixmaps *p = (struct icon_pixmaps *)ref;
|
||||||
|
tll_free_and_free(p->list, free);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
icon_pixmaps_dec(struct icon_pixmaps *p)
|
||||||
|
{
|
||||||
|
ref_dec(&p->refcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct icon_pixmaps *
|
||||||
|
icon_pixmaps_inc(struct icon_pixmaps *p)
|
||||||
|
{
|
||||||
|
ref_inc(&p->refcount);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct icon_pixmaps *
|
||||||
|
new_icon_pixmaps()
|
||||||
|
{
|
||||||
|
struct icon_pixmaps *p = malloc(sizeof(*p));
|
||||||
|
p->refcount = (struct ref){icon_pixmaps_free, 1};
|
||||||
|
icon_pixmaps_t tmp = tll_init();
|
||||||
|
p->list = tmp;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
icon_from_pixmaps(struct icon *icon, struct icon_pixmaps *p)
|
||||||
|
{
|
||||||
|
icon->pixmaps = icon_pixmaps_inc(p);
|
||||||
|
icon->type = ICON_PIXMAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
icon_from_png(struct icon *icon, const char *file_name)
|
||||||
|
{
|
||||||
|
pixman_image_t *png = png_load(file_name);
|
||||||
|
if (png == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
icon->type = ICON_PNG;
|
||||||
|
icon->png = png;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
icon_from_svg(struct icon *icon, const char *file_name)
|
||||||
|
{
|
||||||
|
/* TODO: DPI */
|
||||||
|
NSVGimage *svg = nsvgParseFromFile(file_name, "px", 96);
|
||||||
|
if (svg == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (svg->width == 0 || svg->height == 0) {
|
||||||
|
nsvgDelete(svg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
icon->type = ICON_SVG;
|
||||||
|
icon->svg = svg;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
scale_if_necessary(pixman_image_t *img, int x, int y, int size, pixman_image_t *dest)
|
||||||
|
{
|
||||||
|
pixman_format_code_t fmt = pixman_image_get_format(img);
|
||||||
|
int height = pixman_image_get_height(img);
|
||||||
|
int width = pixman_image_get_width(img);
|
||||||
|
|
||||||
|
bool scale_img = height > size;
|
||||||
|
|
||||||
|
// Exposable is const so have to recalculate every-time...
|
||||||
|
//
|
||||||
|
if (scale_img) {
|
||||||
|
double scale = (double)size / height;
|
||||||
|
|
||||||
|
pixman_f_transform_t _scale_transform;
|
||||||
|
pixman_f_transform_init_scale(&_scale_transform, 1. / scale, 1. / scale);
|
||||||
|
|
||||||
|
pixman_transform_t scale_transform;
|
||||||
|
pixman_transform_from_pixman_f_transform(&scale_transform, &_scale_transform);
|
||||||
|
pixman_image_set_transform(img, &scale_transform);
|
||||||
|
|
||||||
|
int param_count = 0;
|
||||||
|
pixman_kernel_t kernel = PIXMAN_KERNEL_LANCZOS3;
|
||||||
|
pixman_fixed_t *params = pixman_filter_create_separable_convolution(
|
||||||
|
¶m_count, pixman_double_to_fixed(1. / scale), pixman_double_to_fixed(1. / scale), kernel, kernel,
|
||||||
|
kernel, kernel, pixman_int_to_fixed(1), pixman_int_to_fixed(1));
|
||||||
|
|
||||||
|
if (params != NULL || param_count == 0) {
|
||||||
|
pixman_image_set_filter(img, PIXMAN_FILTER_SEPARABLE_CONVOLUTION, params, param_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(params);
|
||||||
|
|
||||||
|
width *= scale;
|
||||||
|
height *= scale;
|
||||||
|
|
||||||
|
int stride = stride_for_format_and_width(fmt, width);
|
||||||
|
uint8_t *data = malloc(height * stride);
|
||||||
|
pixman_image_t *scaled_img = pixman_image_create_bits_no_clear(fmt, width, height, (uint32_t *)data, stride);
|
||||||
|
|
||||||
|
pixman_image_composite32(PIXMAN_OP_SRC, img, NULL, scaled_img, 0, 0, 0, 0, 0, 0, width, height);
|
||||||
|
img = scaled_img;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixman_image_composite32(PIXMAN_OP_OVER, img, NULL, dest, 0, 0, 0, 0, x, y, width, height);
|
||||||
|
|
||||||
|
if (scale_img) {
|
||||||
|
free(pixman_image_get_data(img));
|
||||||
|
pixman_image_unref(img);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
render_png(const struct icon *icon, int x, int y, int size, pixman_image_t *dest)
|
||||||
|
{
|
||||||
|
assert(icon->type == ICON_PNG);
|
||||||
|
assert(icon->png != NULL);
|
||||||
|
|
||||||
|
pixman_image_t *png = icon->png;
|
||||||
|
|
||||||
|
scale_if_necessary(png, x, y, size, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
render_svg(const struct icon *icon, int x, int y, int size, pixman_image_t *dest)
|
||||||
|
{
|
||||||
|
assert(icon->type == ICON_SVG);
|
||||||
|
assert(icon->svg != NULL);
|
||||||
|
|
||||||
|
NSVGimage *svg = icon->svg;
|
||||||
|
struct NSVGrasterizer *rast = nsvgCreateRasterizer();
|
||||||
|
|
||||||
|
if (rast == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
float scale = svg->width > svg->height ? size / svg->width : size / svg->height;
|
||||||
|
|
||||||
|
uint8_t *data = malloc(size * size * 4);
|
||||||
|
nsvgRasterize(rast, svg, 0, 0, scale, data, size, size, size * 4);
|
||||||
|
|
||||||
|
pixman_image_t *img = pixman_image_create_bits_no_clear(PIXMAN_a8b8g8r8, size, size, (uint32_t *)data, size * 4);
|
||||||
|
|
||||||
|
/* Nanosvg produces non-premultiplied ABGR, while pixman expects
|
||||||
|
* premultiplied */
|
||||||
|
for (uint32_t *abgr = (uint32_t *)data; abgr < (uint32_t *)(data + size * size * 4); abgr++) {
|
||||||
|
uint8_t alpha = (*abgr >> 24) & 0xff;
|
||||||
|
uint8_t blue = (*abgr >> 16) & 0xff;
|
||||||
|
uint8_t green = (*abgr >> 8) & 0xff;
|
||||||
|
uint8_t red = (*abgr >> 0) & 0xff;
|
||||||
|
|
||||||
|
if (alpha == 0xff)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (alpha == 0x00)
|
||||||
|
blue = green = red = 0x00;
|
||||||
|
else {
|
||||||
|
blue = blue * alpha / 0xff;
|
||||||
|
green = green * alpha / 0xff;
|
||||||
|
red = red * alpha / 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
*abgr = (uint32_t)alpha << 24 | blue << 16 | green << 8 | red;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsvgDeleteRasterizer(rast);
|
||||||
|
|
||||||
|
pixman_image_composite32(PIXMAN_OP_OVER, img, NULL, dest, 0, 0, 0, 0, x, y, size, size);
|
||||||
|
free(pixman_image_get_data(img));
|
||||||
|
pixman_image_unref(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
render_pixmap(const struct icon *icon, int x, int y, int size, pixman_image_t *dest)
|
||||||
|
{
|
||||||
|
assert(icon->type == ICON_PIXMAP);
|
||||||
|
assert(icon->pixmaps != NULL);
|
||||||
|
|
||||||
|
struct icon_pixmap *icon_pixmap = NULL;
|
||||||
|
int min_error = INT_MAX;
|
||||||
|
tll_foreach(icon->pixmaps->list, it)
|
||||||
|
{
|
||||||
|
int e = abs(size - it->item->size);
|
||||||
|
if (e < min_error) {
|
||||||
|
icon_pixmap = it->item;
|
||||||
|
min_error = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(icon_pixmap != NULL);
|
||||||
|
pixman_image_t *img = pixman_image_create_bits_no_clear(
|
||||||
|
PIXMAN_a8r8g8b8, icon_pixmap->size, icon_pixmap->size, (uint32_t *)icon_pixmap->pixels,
|
||||||
|
stride_for_format_and_width(PIXMAN_a8r8g8b8, icon_pixmap->size));
|
||||||
|
scale_if_necessary(img, x, y, size, dest);
|
||||||
|
pixman_image_unref(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
render_icon(const struct icon *icon, int x, int y, int size, pixman_image_t *dest)
|
||||||
|
{
|
||||||
|
|
||||||
|
switch (icon->type) {
|
||||||
|
case ICON_NONE:
|
||||||
|
break;
|
||||||
|
case ICON_PNG:
|
||||||
|
render_png(icon, x, y, size, dest);
|
||||||
|
break;
|
||||||
|
case ICON_SVG:
|
||||||
|
render_svg(icon, x, y, size, dest);
|
||||||
|
break;
|
||||||
|
case ICON_PIXMAP:
|
||||||
|
render_pixmap(icon, x, y, size, dest);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
reset_icon(struct icon *icon)
|
||||||
|
{
|
||||||
|
switch (icon->type) {
|
||||||
|
case ICON_NONE:
|
||||||
|
break;
|
||||||
|
case ICON_PNG:
|
||||||
|
free(pixman_image_get_data(icon->png));
|
||||||
|
pixman_image_unref(icon->png);
|
||||||
|
icon->png = NULL;
|
||||||
|
break;
|
||||||
|
case ICON_SVG:
|
||||||
|
nsvgDelete(icon->svg);
|
||||||
|
icon->svg = NULL;
|
||||||
|
break;
|
||||||
|
case ICON_PIXMAP:
|
||||||
|
icon_pixmaps_dec(icon->pixmaps);
|
||||||
|
icon->pixmaps = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
icon->type = ICON_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
string_list_free(const struct ref *ref)
|
||||||
|
{
|
||||||
|
struct string_list *b = (struct string_list *)ref;
|
||||||
|
tll_free_and_free(b->strings, free);
|
||||||
|
free(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
string_list_dec(struct string_list *b)
|
||||||
|
{
|
||||||
|
ref_dec(&b->refcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct string_list *
|
||||||
|
string_list_inc(struct string_list *p)
|
||||||
|
{
|
||||||
|
ref_inc(&p->refcount);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct string_list *
|
||||||
|
string_list_new()
|
||||||
|
{
|
||||||
|
string_list_t strings = tll_init();
|
||||||
|
|
||||||
|
struct string_list *out = malloc(sizeof(*out));
|
||||||
|
out->strings = strings;
|
||||||
|
out->refcount = (struct ref){string_list_free, 1};
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
dir_exists(char *path)
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
return stat(path, &sb) == 0 && S_ISDIR(sb.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
destroy_theme(struct icon_theme *theme)
|
||||||
|
{
|
||||||
|
if (!theme) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
free(theme->name);
|
||||||
|
free(theme->comment);
|
||||||
|
tll_free_and_free(theme->inherits, free);
|
||||||
|
tll_free_and_free(theme->directories, free);
|
||||||
|
free(theme->dir);
|
||||||
|
|
||||||
|
tll_foreach(theme->subdirs, it)
|
||||||
|
{
|
||||||
|
free(it->item->name);
|
||||||
|
free(it->item);
|
||||||
|
tll_remove(theme->subdirs, it);
|
||||||
|
}
|
||||||
|
free(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
basedirs_free(const struct ref *ref)
|
||||||
|
{
|
||||||
|
struct basedirs *b = (struct basedirs *)ref;
|
||||||
|
tll_free_and_free(b->basedirs, free);
|
||||||
|
free(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
basedirs_dec(struct basedirs *b)
|
||||||
|
{
|
||||||
|
ref_dec(&b->refcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct basedirs *
|
||||||
|
basedirs_inc(struct basedirs *p)
|
||||||
|
{
|
||||||
|
ref_inc(&p->refcount);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct basedirs *
|
||||||
|
basedirs_new(void)
|
||||||
|
{
|
||||||
|
string_list_t basedirs = tll_init();
|
||||||
|
|
||||||
|
struct basedirs *out = malloc(sizeof(*out));
|
||||||
|
out->basedirs = basedirs;
|
||||||
|
out->refcount = (struct ref){basedirs_free, 1};
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct basedirs *
|
||||||
|
get_basedirs(void)
|
||||||
|
{
|
||||||
|
string_list_t basedirs = tll_init();
|
||||||
|
|
||||||
|
// Follows freedesktop specs:
|
||||||
|
//
|
||||||
|
tll_push_back(basedirs, strdup("$HOME/.icons")); // deprecated
|
||||||
|
|
||||||
|
char *data_home = getenv("XDG_DATA_HOME");
|
||||||
|
tll_push_back(basedirs, strdup(data_home && *data_home ? "$XDG_DATA_HOME/icons" : "$HOME/.local/share/icons"));
|
||||||
|
|
||||||
|
tll_push_back(basedirs, strdup("/usr/share/pixmaps"));
|
||||||
|
|
||||||
|
char *data_dirs = getenv("XDG_DATA_DIRS");
|
||||||
|
if (!(data_dirs && *data_dirs)) {
|
||||||
|
data_dirs = "/usr/local/share:/usr/share";
|
||||||
|
}
|
||||||
|
data_dirs = strdup(data_dirs);
|
||||||
|
char *dir = strtok(data_dirs, ":");
|
||||||
|
do {
|
||||||
|
char *path = format_str("%s/icons", dir);
|
||||||
|
tll_push_back(basedirs, path);
|
||||||
|
} while ((dir = strtok(NULL, ":")));
|
||||||
|
free(data_dirs);
|
||||||
|
|
||||||
|
string_list_t basedirs_expanded = tll_init();
|
||||||
|
tll_foreach(basedirs, it)
|
||||||
|
{
|
||||||
|
wordexp_t p;
|
||||||
|
if (wordexp(it->item, &p, WRDE_UNDEF) == 0) {
|
||||||
|
if (dir_exists(p.we_wordv[0])) {
|
||||||
|
tll_push_back(basedirs_expanded, strdup(p.we_wordv[0]));
|
||||||
|
}
|
||||||
|
wordfree(&p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tll_free_and_free(basedirs, free);
|
||||||
|
struct basedirs *out = malloc(sizeof(*out));
|
||||||
|
out->basedirs = basedirs_expanded;
|
||||||
|
out->refcount = (struct ref){basedirs_free, 1};
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
group_handler(char *old_group, char *new_group, struct icon_theme *theme)
|
||||||
|
{
|
||||||
|
if (!old_group) {
|
||||||
|
return new_group && strcasecmp(new_group, "icon theme") == 0 ? NULL : "first group must be 'Icon Theme'";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcasecmp(old_group, "icon theme") == 0) {
|
||||||
|
if (!theme->name) {
|
||||||
|
return "missing required key 'Name'";
|
||||||
|
} else if (!theme->comment) {
|
||||||
|
return "missing required key 'Comment'";
|
||||||
|
} else if (tll_length(theme->directories) == 0) {
|
||||||
|
return "missing required key 'Directories'";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (tll_length(theme->subdirs) == 0) { // skip
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct icon_theme_subdir *subdir = tll_back(theme->subdirs);
|
||||||
|
if (!subdir->size) {
|
||||||
|
return "missing required key 'Size'";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (subdir->type) {
|
||||||
|
case ICON_DIR_FIXED:
|
||||||
|
subdir->max_size = subdir->min_size = subdir->size;
|
||||||
|
break;
|
||||||
|
case ICON_DIR_SCALABLE: {
|
||||||
|
if (!subdir->max_size)
|
||||||
|
subdir->max_size = subdir->size;
|
||||||
|
if (!subdir->min_size)
|
||||||
|
subdir->min_size = subdir->size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ICON_DIR_THRESHOLD:
|
||||||
|
subdir->max_size = subdir->size + subdir->threshold;
|
||||||
|
subdir->min_size = subdir->size - subdir->threshold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_group) {
|
||||||
|
bool found = false;
|
||||||
|
tll_foreach(theme->directories, it)
|
||||||
|
{
|
||||||
|
if (strcmp(it->item, new_group)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
struct icon_theme_subdir *subdir = calloc(1, sizeof(struct icon_theme_subdir));
|
||||||
|
if (!subdir) {
|
||||||
|
return "out of memory";
|
||||||
|
}
|
||||||
|
subdir->name = strdup(new_group);
|
||||||
|
subdir->threshold = 2;
|
||||||
|
tll_push_back(theme->subdirs, subdir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_list_t
|
||||||
|
split_string(const char *str, const char *delims)
|
||||||
|
{
|
||||||
|
string_list_t res = tll_init();
|
||||||
|
char *copy = strdup(str);
|
||||||
|
|
||||||
|
char *token = strtok(copy, delims);
|
||||||
|
while (token) {
|
||||||
|
tll_push_back(res, strdup(token));
|
||||||
|
token = strtok(NULL, delims);
|
||||||
|
}
|
||||||
|
free(copy);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OOM_ERROR(val) \
|
||||||
|
if (!val) \
|
||||||
|
return "out of memory";
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
entry_handler(char *group, char *key, char *value, struct icon_theme *theme)
|
||||||
|
{
|
||||||
|
if (strcmp(group, "Icon Theme") == 0) {
|
||||||
|
if (strcmp(key, "Name") == 0) {
|
||||||
|
theme->name = strdup(value);
|
||||||
|
OOM_ERROR(theme->name);
|
||||||
|
} else if (strcmp(key, "Comment") == 0) {
|
||||||
|
theme->comment = strdup(value);
|
||||||
|
OOM_ERROR(theme->comment);
|
||||||
|
} else if (strcmp(key, "Inherits") == 0) {
|
||||||
|
theme->inherits = split_string(value, ",");
|
||||||
|
} else if (strcmp(key, "Directories") == 0) {
|
||||||
|
theme->directories = split_string(value, ",");
|
||||||
|
} // Ignored: ScaledDirectories, Hidden, Example
|
||||||
|
} else {
|
||||||
|
if (tll_length(theme->subdirs) == 0) { // skip
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct icon_theme_subdir *subdir = tll_back(theme->subdirs);
|
||||||
|
if (strcmp(subdir->name, group) != 0) { // skip
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(key, "Context") == 0) {
|
||||||
|
return NULL; // ignored, but explicitly handled to not fail parsing
|
||||||
|
} else if (strcmp(key, "Type") == 0) {
|
||||||
|
if (strcmp(value, "Fixed") == 0) {
|
||||||
|
subdir->type = ICON_DIR_FIXED;
|
||||||
|
} else if (strcmp(value, "Scalable") == 0) {
|
||||||
|
subdir->type = ICON_DIR_SCALABLE;
|
||||||
|
} else if (strcmp(value, "Threshold") == 0) {
|
||||||
|
subdir->type = ICON_DIR_THRESHOLD;
|
||||||
|
} else {
|
||||||
|
return "invalid value - expected 'Fixed', 'Scalable', or 'Threshold";
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *end;
|
||||||
|
int n = strtol(value, &end, 10);
|
||||||
|
if (*end != '\0') {
|
||||||
|
return "invalid value - expected a number";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(key, "Size") == 0) {
|
||||||
|
subdir->size = n;
|
||||||
|
} else if (strcmp(key, "MaxSize") == 0) {
|
||||||
|
subdir->max_size = n;
|
||||||
|
} else if (strcmp(key, "MinSize") == 0) {
|
||||||
|
subdir->min_size = n;
|
||||||
|
} else if (strcmp(key, "Threshold") == 0) {
|
||||||
|
subdir->threshold = n;
|
||||||
|
} else if (strcmp(key, "Scale") == 0) {
|
||||||
|
subdir->scale = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a Freedesktop Desktop Entry parser (essentially INI)
|
||||||
|
* It calls entry_handler for every entry
|
||||||
|
* and group_handler between every group (as well as at both ends)
|
||||||
|
* Handlers return whether an error occurred, which stops parsing
|
||||||
|
*/
|
||||||
|
struct icon_theme *
|
||||||
|
read_theme_file(char *basedir, char *theme_name)
|
||||||
|
{
|
||||||
|
struct icon_theme *theme = calloc(1, sizeof(struct icon_theme));
|
||||||
|
if (!theme) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
string_list_t tmp1 = tll_init();
|
||||||
|
subdirs_t tmp3 = tll_init();
|
||||||
|
theme->directories = tmp1;
|
||||||
|
theme->inherits = tmp1;
|
||||||
|
theme->subdirs = tmp3;
|
||||||
|
|
||||||
|
// look for index.theme file
|
||||||
|
char *path = format_str("%s/%s/index.theme", basedir, theme_name);
|
||||||
|
if (!path) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
FILE *theme_file = fopen(path, "r");
|
||||||
|
free(path);
|
||||||
|
if (!theme_file) {
|
||||||
|
free(theme);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_list_t groups = tll_init();
|
||||||
|
char *full_line = NULL;
|
||||||
|
const char *error = NULL;
|
||||||
|
int line_no = 0;
|
||||||
|
size_t sz = 0;
|
||||||
|
while (true) {
|
||||||
|
const char *warning = NULL;
|
||||||
|
ssize_t nread = getline(&full_line, &sz, theme_file);
|
||||||
|
if (nread == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++line_no;
|
||||||
|
|
||||||
|
char *line = full_line - 1;
|
||||||
|
while (isspace(*++line)) {
|
||||||
|
} // remove leading whitespace
|
||||||
|
if (!*line || line[0] == '#')
|
||||||
|
goto next; // ignore blank lines & comments
|
||||||
|
|
||||||
|
int len = nread - (line - full_line);
|
||||||
|
while (isspace(line[--len])) {
|
||||||
|
}
|
||||||
|
line[++len] = '\0'; // Remove trailing whitespace
|
||||||
|
|
||||||
|
if (line[0] == '[') { // Group handler
|
||||||
|
// check well-formed
|
||||||
|
int i = 1;
|
||||||
|
for (; !iscntrl(line[i]) && line[i] != '[' && line[i] != ']'; ++i) {
|
||||||
|
}
|
||||||
|
if (i != --len || line[i] != ']') {
|
||||||
|
warning = "malformed group header";
|
||||||
|
goto warn;
|
||||||
|
}
|
||||||
|
|
||||||
|
line[len] = '\0';
|
||||||
|
line = &line[1];
|
||||||
|
|
||||||
|
tll_foreach(groups, it)
|
||||||
|
{
|
||||||
|
// If duplicate move to back and continue
|
||||||
|
//
|
||||||
|
if (strcmp(it->item, line) == 0) {
|
||||||
|
tll_push_back(groups, it->item);
|
||||||
|
tll_remove(groups, it);
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *last_group = tll_length(groups) ? tll_back(groups) : NULL;
|
||||||
|
error = group_handler(last_group, line, theme);
|
||||||
|
if (error) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tll_push_back(groups, strdup(line));
|
||||||
|
} else {
|
||||||
|
if (tll_length(groups) == 0) {
|
||||||
|
error = "unexpected content before first header";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check well-formed
|
||||||
|
int eok = 0;
|
||||||
|
for (; isalnum(line[eok]) || line[eok] == '-'; ++eok) {
|
||||||
|
}
|
||||||
|
int i = eok - 1;
|
||||||
|
while (isspace(line[++i])) {
|
||||||
|
}
|
||||||
|
if (line[i] == '[') {
|
||||||
|
// not handling localized values:
|
||||||
|
// https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s05.html
|
||||||
|
//
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
if (line[i] != '=') {
|
||||||
|
warning = "malformed key-value pair";
|
||||||
|
goto warn;
|
||||||
|
}
|
||||||
|
|
||||||
|
line[eok] = '\0'; // split into key-value pair
|
||||||
|
|
||||||
|
char *value = &line[i];
|
||||||
|
while (isspace(*++value)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
error = entry_handler(tll_back(groups), line, value, theme);
|
||||||
|
if (error) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next:
|
||||||
|
continue;
|
||||||
|
|
||||||
|
warn:;
|
||||||
|
assert(warning != NULL);
|
||||||
|
char *group = tll_length(groups) > 0 ? tll_back(groups) : "n/a";
|
||||||
|
LOG_INFO("Error during load of theme '%s' - parsing of file "
|
||||||
|
"'%s/%s/index.theme' encountered '%s' on line %d (group '%s') - continuing",
|
||||||
|
theme_name, basedir, theme_name, warning, line_no, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
if (tll_length(groups) > 0) {
|
||||||
|
error = group_handler(tll_back(groups), NULL, theme);
|
||||||
|
} else {
|
||||||
|
error = "empty file";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
char *group = tll_length(groups) > 0 ? tll_back(groups) : "n/a";
|
||||||
|
LOG_WARN("Failed to load theme '%s' - parsing of file "
|
||||||
|
"'%s/%s/index.theme' failed on line %d (group '%s'): %s",
|
||||||
|
theme_name, basedir, theme_name, line_no, group, error);
|
||||||
|
destroy_theme(theme);
|
||||||
|
theme = NULL;
|
||||||
|
} else {
|
||||||
|
theme->dir = strdup(theme_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(full_line);
|
||||||
|
fclose(theme_file);
|
||||||
|
tll_free_and_free(groups, free);
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
themes_t
|
||||||
|
load_themes_in_dir(char *basedir)
|
||||||
|
{
|
||||||
|
themes_t themes = tll_init();
|
||||||
|
|
||||||
|
DIR *dir;
|
||||||
|
if (!(dir = opendir(basedir))) {
|
||||||
|
return themes;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent *entry;
|
||||||
|
while ((entry = readdir(dir))) {
|
||||||
|
if (entry->d_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
struct icon_theme *theme = read_theme_file(basedir, entry->d_name);
|
||||||
|
if (theme) {
|
||||||
|
LOG_DBG("Found theme [%s]: dir: %s", theme->name, theme->dir);
|
||||||
|
tll_push_back(themes, theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
return themes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
log_loaded_themes(themes_t themes)
|
||||||
|
{
|
||||||
|
LOG_INFO("Logging themes");
|
||||||
|
if (tll_length(themes) == 0) {
|
||||||
|
LOG_INFO("Warning: no icon themes loaded");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const char sep[] = ", ";
|
||||||
|
size_t sep_len = strlen(sep);
|
||||||
|
|
||||||
|
size_t len = 0;
|
||||||
|
tll_foreach(themes, it) { len += strlen(it->item->name) + sep_len; }
|
||||||
|
|
||||||
|
char *str = malloc(len + 1);
|
||||||
|
if (!str) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char *p = str;
|
||||||
|
bool start = true;
|
||||||
|
tll_foreach(themes, it)
|
||||||
|
{
|
||||||
|
if (!start) {
|
||||||
|
memcpy(p, sep, sep_len);
|
||||||
|
p += sep_len;
|
||||||
|
}
|
||||||
|
start = false;
|
||||||
|
|
||||||
|
struct icon_theme *theme = it->item;
|
||||||
|
size_t name_len = strlen(theme->name);
|
||||||
|
memcpy(p, theme->name, name_len);
|
||||||
|
p += name_len;
|
||||||
|
}
|
||||||
|
*p = '\0';
|
||||||
|
|
||||||
|
LOG_INFO("Loaded icon themes: %s", str);
|
||||||
|
free(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
themes_free(const struct ref *ref)
|
||||||
|
{
|
||||||
|
LOG_DBG("themes free");
|
||||||
|
struct themes *p = (struct themes *)ref;
|
||||||
|
tll_free_and_free(p->themes, destroy_theme);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
themes_dec(struct themes *t)
|
||||||
|
{
|
||||||
|
ref_dec(&t->refcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct themes *
|
||||||
|
themes_inc(struct themes *t)
|
||||||
|
{
|
||||||
|
ref_inc(&t->refcount);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct themes *
|
||||||
|
init_themes(struct basedirs *basedirs)
|
||||||
|
{
|
||||||
|
|
||||||
|
themes_t themes = tll_init();
|
||||||
|
tll_foreach(basedirs->basedirs, it)
|
||||||
|
{
|
||||||
|
themes_t dir_themes = load_themes_in_dir(it->item);
|
||||||
|
tll_foreach(dir_themes, it)
|
||||||
|
{
|
||||||
|
struct icon_theme *theme = it->item;
|
||||||
|
tll_remove(dir_themes, it);
|
||||||
|
tll_push_back(themes, theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_loaded_themes(themes);
|
||||||
|
struct themes *out = malloc(sizeof(*out));
|
||||||
|
out->themes = themes;
|
||||||
|
out->refcount = (struct ref){themes_free, 1};
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
find_icon_in_subdir(char *name, char *basedir, char *theme, char *subdir)
|
||||||
|
{
|
||||||
|
static const char *extensions[] = {
|
||||||
|
"svg",
|
||||||
|
"png",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(extensions) / sizeof(*extensions); ++i) {
|
||||||
|
char *path = format_str("%s/%s/%s/%s.%s", basedir, theme, subdir, name, extensions[i]);
|
||||||
|
if (path && access(path, R_OK) == 0) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
free(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
theme_exists_in_basedir(char *theme, char *basedir)
|
||||||
|
{
|
||||||
|
char *path = format_str("%s/%s", basedir, theme);
|
||||||
|
bool ret = dir_exists(path);
|
||||||
|
free(path);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
find_icon_with_theme(string_list_t basedirs, themes_t themes, char *name, int size, char *theme_name, int *min_size,
|
||||||
|
int *max_size)
|
||||||
|
{
|
||||||
|
LOG_DBG("Looking for icon [%s] in theme [%s]", name, theme_name);
|
||||||
|
struct icon_theme *theme = NULL;
|
||||||
|
tll_foreach(themes, it)
|
||||||
|
{
|
||||||
|
theme = it->item;
|
||||||
|
if (strcmp(theme->name, theme_name) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
theme = NULL;
|
||||||
|
}
|
||||||
|
if (!theme)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char *icon = NULL;
|
||||||
|
tll_foreach(basedirs, bd_it)
|
||||||
|
{
|
||||||
|
if (!theme_exists_in_basedir(theme->dir, bd_it->item)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
tll_rforeach(theme->subdirs, sd_it)
|
||||||
|
{
|
||||||
|
struct icon_theme_subdir *subdir = sd_it->item;
|
||||||
|
if (size >= subdir->min_size && size <= subdir->max_size) {
|
||||||
|
if ((icon = find_icon_in_subdir(name, bd_it->item, theme->dir, subdir->name))) {
|
||||||
|
*min_size = subdir->min_size;
|
||||||
|
*max_size = subdir->max_size;
|
||||||
|
LOG_DBG("Found icon [%s] in theme [%s]", name, theme_name);
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// inexact match
|
||||||
|
unsigned smallest_error = -1; // UINT_MAX
|
||||||
|
tll_foreach(basedirs, bd_it)
|
||||||
|
{
|
||||||
|
if (!theme_exists_in_basedir(theme->dir, bd_it->item)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
tll_rforeach(theme->subdirs, sd_it)
|
||||||
|
{
|
||||||
|
struct icon_theme_subdir *subdir = sd_it->item;
|
||||||
|
unsigned error = (size > subdir->max_size ? size - subdir->max_size : 0)
|
||||||
|
+ (size < subdir->min_size ? subdir->min_size - size : 0);
|
||||||
|
if (error < smallest_error) {
|
||||||
|
char *test_icon = find_icon_in_subdir(name, bd_it->item, theme->dir, subdir->name);
|
||||||
|
if (test_icon) {
|
||||||
|
if (icon)
|
||||||
|
free(icon);
|
||||||
|
icon = test_icon;
|
||||||
|
smallest_error = error;
|
||||||
|
*min_size = subdir->min_size;
|
||||||
|
*max_size = subdir->max_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!icon) {
|
||||||
|
tll_foreach(theme->inherits, it)
|
||||||
|
{
|
||||||
|
icon = find_icon_with_theme(basedirs, themes, name, size, it->item, min_size, max_size);
|
||||||
|
if (icon) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
find_fallback_icon(string_list_t basedirs, char *name, int *min_size, int *max_size)
|
||||||
|
{
|
||||||
|
tll_foreach(basedirs, it)
|
||||||
|
{
|
||||||
|
char *icon = find_icon_in_subdir(name, it->item, "", "");
|
||||||
|
if (icon) {
|
||||||
|
*min_size = 1;
|
||||||
|
*max_size = 512;
|
||||||
|
LOG_DBG("Found icon [%s] in fallback theme [%s]", name, it->item);
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
find_icon(themes_t themes, string_list_t basedirs, char *name, int size, string_list_t icon_themes, int *min_size,
|
||||||
|
int *max_size)
|
||||||
|
{
|
||||||
|
// TODO https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#implementation_notes
|
||||||
|
//
|
||||||
|
char *icon = NULL;
|
||||||
|
bool seenHicolor = false;
|
||||||
|
tll_foreach(icon_themes, it)
|
||||||
|
{
|
||||||
|
icon = find_icon_with_theme(basedirs, themes, name, size, it->item, min_size, max_size);
|
||||||
|
if (icon) {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
seenHicolor |= strcmp(it->item, "Hicolor") == 0;
|
||||||
|
}
|
||||||
|
if (!seenHicolor) {
|
||||||
|
icon = find_icon_with_theme(basedirs, themes, name, size, "Hicolor", min_size, max_size);
|
||||||
|
}
|
||||||
|
if (!icon) {
|
||||||
|
icon = find_fallback_icon(basedirs, name, min_size, max_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return icon;
|
||||||
|
}
|
122
icon.h
Normal file
122
icon.h
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <nanosvg/nanosvg.h>
|
||||||
|
#include <pixman.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <tllist.h>
|
||||||
|
|
||||||
|
struct ref {
|
||||||
|
void (*free)(const struct ref *);
|
||||||
|
atomic_size_t count;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
ref_inc(const struct ref *ref)
|
||||||
|
{
|
||||||
|
atomic_fetch_add((atomic_size_t *)&ref->count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
ref_dec(const struct ref *ref)
|
||||||
|
{
|
||||||
|
if (atomic_fetch_sub((atomic_size_t *)&ref->count, 1) == 1) {
|
||||||
|
ref->free(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct icon_pixmap {
|
||||||
|
int size;
|
||||||
|
unsigned char pixels[];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef tll(struct icon_pixmap *) icon_pixmaps_t;
|
||||||
|
|
||||||
|
struct icon_pixmaps {
|
||||||
|
struct ref refcount;
|
||||||
|
icon_pixmaps_t list;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum icon_type { ICON_NONE, ICON_PNG, ICON_SVG, ICON_PIXMAP };
|
||||||
|
|
||||||
|
struct icon {
|
||||||
|
enum icon_type type;
|
||||||
|
union {
|
||||||
|
NSVGimage *svg;
|
||||||
|
pixman_image_t *png;
|
||||||
|
struct icon_pixmaps *pixmaps;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
enum icon_dir_type { ICON_DIR_FIXED, ICON_DIR_SCALABLE, ICON_DIR_THRESHOLD };
|
||||||
|
|
||||||
|
struct icon_theme_subdir {
|
||||||
|
char *name;
|
||||||
|
char *context;
|
||||||
|
|
||||||
|
int size;
|
||||||
|
int max_size;
|
||||||
|
int min_size;
|
||||||
|
int scale;
|
||||||
|
int threshold;
|
||||||
|
enum icon_dir_type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef tll(struct icon_theme *) themes_t;
|
||||||
|
typedef tll(char *) string_list_t;
|
||||||
|
typedef tll(struct icon_theme_subdir *) subdirs_t;
|
||||||
|
|
||||||
|
struct themes {
|
||||||
|
struct ref refcount;
|
||||||
|
themes_t themes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct basedirs {
|
||||||
|
struct ref refcount;
|
||||||
|
string_list_t basedirs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct string_list {
|
||||||
|
struct ref refcount;
|
||||||
|
string_list_t strings;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct icon_theme {
|
||||||
|
char *name;
|
||||||
|
char *comment;
|
||||||
|
string_list_t inherits; // char *
|
||||||
|
string_list_t directories; // char *
|
||||||
|
|
||||||
|
char *dir;
|
||||||
|
subdirs_t subdirs; // struct icon_theme_subdir *
|
||||||
|
};
|
||||||
|
|
||||||
|
bool dir_exists(char *path);
|
||||||
|
|
||||||
|
void icon_from_pixmaps(struct icon *icon, struct icon_pixmaps *p);
|
||||||
|
bool icon_from_png(struct icon *icon, const char *file_name);
|
||||||
|
bool icon_from_svg(struct icon *icon, const char *file_name);
|
||||||
|
void render_icon(const struct icon *icon, int x, int y, int size, pixman_image_t *dest);
|
||||||
|
void reset_icon(struct icon *icon);
|
||||||
|
|
||||||
|
struct string_list *string_list_new();
|
||||||
|
void string_list_dec(struct string_list *s);
|
||||||
|
struct string_list *string_list_inc(struct string_list *s);
|
||||||
|
|
||||||
|
struct icon_pixmaps *new_icon_pixmaps();
|
||||||
|
void icon_pixmaps_dec(struct icon_pixmaps *p);
|
||||||
|
struct icon_pixmaps *icon_pixmaps_inc(struct icon_pixmaps *ip);
|
||||||
|
|
||||||
|
struct basedirs *get_basedirs();
|
||||||
|
struct basedirs *basedirs_new();
|
||||||
|
void basedirs_dec(struct basedirs *p);
|
||||||
|
struct basedirs *basedirs_inc(struct basedirs *p);
|
||||||
|
|
||||||
|
struct themes *init_themes(struct basedirs *basedirs);
|
||||||
|
void themes_dec(struct themes *p);
|
||||||
|
struct themes *themes_inc(struct themes *p);
|
||||||
|
|
||||||
|
char *find_icon(themes_t themes, string_list_t basedirs, char *name, int size, string_list_t icon_themes, int *min_size,
|
||||||
|
int *max_size);
|
||||||
|
|
||||||
|
void log_loaded_themes(themes_t themes);
|
19
meson.build
19
meson.build
|
@ -52,6 +52,20 @@ libepoll = dependency('epoll-shim', required: false)
|
||||||
libinotify = dependency('libinotify', required: false)
|
libinotify = dependency('libinotify', required: false)
|
||||||
pixman = dependency('pixman-1')
|
pixman = dependency('pixman-1')
|
||||||
yaml = dependency('yaml-0.1')
|
yaml = dependency('yaml-0.1')
|
||||||
|
system_nanosvg = cc.find_library('nanosvg', required: get_option('system-nanosvg'))
|
||||||
|
system_nanosvgrast = cc.find_library('nanosvgrast', required: get_option('system-nanosvg'))
|
||||||
|
if system_nanosvg.found() and system_nanosvgrast.found()
|
||||||
|
nanosvg = declare_dependency(
|
||||||
|
dependencies: [system_nanosvg, system_nanosvgrast]
|
||||||
|
)
|
||||||
|
else
|
||||||
|
nanosvg = declare_dependency(
|
||||||
|
sources: ['nanosvg.c', '3rd-party/nanosvg/src/nanosvg.h',
|
||||||
|
'nanosvgrast.c', '3rd-party/nanosvg/src/nanosvgrast.h'],
|
||||||
|
include_directories: '.',
|
||||||
|
dependencies: m)
|
||||||
|
endif
|
||||||
|
png = dependency('libpng')
|
||||||
|
|
||||||
# X11/XCB dependencies
|
# X11/XCB dependencies
|
||||||
xcb_aux = dependency('xcb-aux', required: get_option('backend-x11'))
|
xcb_aux = dependency('xcb-aux', required: get_option('backend-x11'))
|
||||||
|
@ -128,8 +142,11 @@ yambar = executable(
|
||||||
'plugin.c', 'plugin.h',
|
'plugin.c', 'plugin.h',
|
||||||
'tag.c', 'tag.h',
|
'tag.c', 'tag.h',
|
||||||
'yml.c', 'yml.h',
|
'yml.c', 'yml.h',
|
||||||
|
'icon.c', 'icon.h',
|
||||||
|
'png.c', 'png-yambar.h',
|
||||||
|
'stringop.c', 'stringop.h',
|
||||||
version,
|
version,
|
||||||
dependencies: [bar, libepoll, libinotify, pixman, yaml, threads, dl, tllist, fcft] +
|
dependencies: [bar, libepoll, libinotify, pixman, yaml, nanosvg, png, threads, dl, tllist, fcft] +
|
||||||
decorations + particles + modules,
|
decorations + particles + modules,
|
||||||
build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles',
|
build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles',
|
||||||
export_dynamic: true,
|
export_dynamic: true,
|
||||||
|
|
|
@ -48,3 +48,5 @@ option('plugin-xkb', type: 'feature', value: 'auto',
|
||||||
description: 'keyboard support for X11')
|
description: 'keyboard support for X11')
|
||||||
option('plugin-xwindow', type: 'feature', value: 'auto',
|
option('plugin-xwindow', type: 'feature', value: 'auto',
|
||||||
description: 'XWindow (window tracking for X11) support')
|
description: 'XWindow (window tracking for X11) support')
|
||||||
|
option('system-nanosvg', type: 'feature', value: 'disabled',
|
||||||
|
description: 'use system\'s nanosvg instead of the bundled version')
|
||||||
|
|
6
nanosvg.c
Normal file
6
nanosvg.c
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#define NANOSVG_ALL_COLOR_KEYWORDS
|
||||||
|
#define NANOSVG_IMPLEMENTATION
|
||||||
|
#include <3rd-party/nanosvg/src/nanosvg.h>
|
1
nanosvg/nanosvg.h
Normal file
1
nanosvg/nanosvg.h
Normal file
|
@ -0,0 +1 @@
|
||||||
|
#include <3rd-party/nanosvg/src/nanosvg.h>
|
1
nanosvg/nanosvgrast.h
Normal file
1
nanosvg/nanosvgrast.h
Normal file
|
@ -0,0 +1 @@
|
||||||
|
#include <3rd-party/nanosvg/src/nanosvgrast.h>
|
6
nanosvgrast.c
Normal file
6
nanosvgrast.c
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <3rd-party/nanosvg/src/nanosvg.h>
|
||||||
|
#define NANOSVGRAST_IMPLEMENTATION
|
||||||
|
#include <3rd-party/nanosvg/src/nanosvgrast.h>
|
12
particle.c
12
particle.c
|
@ -15,6 +15,7 @@
|
||||||
#define LOG_MODULE "particle"
|
#define LOG_MODULE "particle"
|
||||||
#define LOG_ENABLE_DBG 0
|
#define LOG_ENABLE_DBG 0
|
||||||
#include "bar/bar.h"
|
#include "bar/bar.h"
|
||||||
|
#include "icon.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -25,12 +26,17 @@ particle_default_destroy(struct particle *particle)
|
||||||
fcft_destroy(particle->font);
|
fcft_destroy(particle->font);
|
||||||
for (size_t i = 0; i < MOUSE_BTN_COUNT; i++)
|
for (size_t i = 0; i < MOUSE_BTN_COUNT; i++)
|
||||||
free(particle->on_click_templates[i]);
|
free(particle->on_click_templates[i]);
|
||||||
|
|
||||||
|
themes_dec(particle->themes);
|
||||||
|
basedirs_dec(particle->basedirs);
|
||||||
|
string_list_dec(particle->icon_themes);
|
||||||
free(particle);
|
free(particle);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct particle *
|
struct particle *
|
||||||
particle_common_new(int left_margin, int right_margin, char **on_click_templates, struct fcft_font *font,
|
particle_common_new(int left_margin, int right_margin, char **on_click_templates, struct fcft_font *font,
|
||||||
enum font_shaping font_shaping, pixman_color_t foreground, struct deco *deco)
|
enum font_shaping font_shaping, struct basedirs *basedirs, struct themes *themes,
|
||||||
|
struct string_list *icon_themes, int icon_size, pixman_color_t foreground, struct deco *deco)
|
||||||
{
|
{
|
||||||
struct particle *p = calloc(1, sizeof(*p));
|
struct particle *p = calloc(1, sizeof(*p));
|
||||||
p->left_margin = left_margin;
|
p->left_margin = left_margin;
|
||||||
|
@ -39,6 +45,10 @@ particle_common_new(int left_margin, int right_margin, char **on_click_templates
|
||||||
p->font = font;
|
p->font = font;
|
||||||
p->font_shaping = font_shaping;
|
p->font_shaping = font_shaping;
|
||||||
p->deco = deco;
|
p->deco = deco;
|
||||||
|
p->basedirs = basedirs;
|
||||||
|
p->themes = themes;
|
||||||
|
p->icon_themes = icon_themes;
|
||||||
|
p->icon_size = icon_size;
|
||||||
|
|
||||||
if (on_click_templates != NULL) {
|
if (on_click_templates != NULL) {
|
||||||
for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) {
|
for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) {
|
||||||
|
|
13
particle.h
13
particle.h
|
@ -6,6 +6,7 @@
|
||||||
#include "color.h"
|
#include "color.h"
|
||||||
#include "decoration.h"
|
#include "decoration.h"
|
||||||
#include "font-shaping.h"
|
#include "font-shaping.h"
|
||||||
|
#include "icon.h"
|
||||||
#include "tag.h"
|
#include "tag.h"
|
||||||
|
|
||||||
enum mouse_event {
|
enum mouse_event {
|
||||||
|
@ -41,6 +42,11 @@ struct particle {
|
||||||
enum font_shaping font_shaping;
|
enum font_shaping font_shaping;
|
||||||
struct deco *deco;
|
struct deco *deco;
|
||||||
|
|
||||||
|
struct themes *themes;
|
||||||
|
struct basedirs *basedirs;
|
||||||
|
struct string_list *icon_themes;
|
||||||
|
int icon_size;
|
||||||
|
|
||||||
void (*destroy)(struct particle *particle);
|
void (*destroy)(struct particle *particle);
|
||||||
struct exposable *(*instantiate)(const struct particle *particle, const struct tag_set *tags);
|
struct exposable *(*instantiate)(const struct particle *particle, const struct tag_set *tags);
|
||||||
};
|
};
|
||||||
|
@ -61,8 +67,9 @@ struct exposable {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct particle *particle_common_new(int left_margin, int right_margin, char *on_click_templates[],
|
struct particle *particle_common_new(int left_margin, int right_margin, char *on_click_templates[],
|
||||||
struct fcft_font *font, enum font_shaping font_shaping, pixman_color_t foreground,
|
struct fcft_font *font, enum font_shaping font_shaping, struct basedirs *basedirs,
|
||||||
struct deco *deco);
|
struct themes *themes, struct string_list *icon_themes, int icon_size,
|
||||||
|
pixman_color_t foreground, struct deco *deco);
|
||||||
|
|
||||||
void particle_default_destroy(struct particle *particle);
|
void particle_default_destroy(struct particle *particle);
|
||||||
|
|
||||||
|
@ -79,6 +86,8 @@ void exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, en
|
||||||
{"right-margin", false, &conf_verify_unsigned}, {"on-click", false, &conf_verify_on_click}, \
|
{"right-margin", false, &conf_verify_unsigned}, {"on-click", false, &conf_verify_on_click}, \
|
||||||
{"font", false, &conf_verify_font}, {"font-shaping", false, &conf_verify_font_shaping}, \
|
{"font", false, &conf_verify_font}, {"font-shaping", false, &conf_verify_font_shaping}, \
|
||||||
{"foreground", false, &conf_verify_color}, {"deco", false, &conf_verify_decoration}, \
|
{"foreground", false, &conf_verify_color}, {"deco", false, &conf_verify_decoration}, \
|
||||||
|
{"icon-basedirs", false, &conf_verify_string_list}, {"icon-themes", false, &conf_verify_string_list}, \
|
||||||
|
{"icon-size", false, &conf_verify_unsigned}, \
|
||||||
{ \
|
{ \
|
||||||
NULL, false, NULL \
|
NULL, false, NULL \
|
||||||
}
|
}
|
||||||
|
|
206
particles/icon.c
Normal file
206
particles/icon.c
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "particles/icon"
|
||||||
|
#define LOG_ENABLE_DBG 0
|
||||||
|
#include "../config-verify.h"
|
||||||
|
#include "../config.h"
|
||||||
|
#include "../icon.h"
|
||||||
|
#include "../log.h"
|
||||||
|
#include "../particle.h"
|
||||||
|
#include "../plugin.h"
|
||||||
|
#include "../tag.h"
|
||||||
|
|
||||||
|
struct private
|
||||||
|
{
|
||||||
|
char *text;
|
||||||
|
bool use_tag;
|
||||||
|
struct particle *fallback;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct eprivate {
|
||||||
|
struct exposable *exposable;
|
||||||
|
struct icon icon;
|
||||||
|
pixman_image_t *rasterized;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
exposable_destroy(struct exposable *exposable)
|
||||||
|
{
|
||||||
|
struct eprivate *e = exposable->private;
|
||||||
|
if (e->exposable)
|
||||||
|
e->exposable->destroy(e->exposable);
|
||||||
|
|
||||||
|
reset_icon(&e->icon);
|
||||||
|
free(e);
|
||||||
|
exposable_default_destroy(exposable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
begin_expose(struct exposable *exposable)
|
||||||
|
{
|
||||||
|
struct eprivate *e = exposable->private;
|
||||||
|
|
||||||
|
if (e->icon.type == ICON_NONE) {
|
||||||
|
if (e->exposable) {
|
||||||
|
exposable->width = e->exposable->begin_expose(e->exposable);
|
||||||
|
} else {
|
||||||
|
exposable->width = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert(e->exposable == NULL);
|
||||||
|
exposable->width = exposable->particle->right_margin + exposable->particle->left_margin;
|
||||||
|
exposable->width += exposable->particle->icon_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return exposable->width;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height)
|
||||||
|
{
|
||||||
|
exposable_render_deco(exposable, pix, x, y, height);
|
||||||
|
|
||||||
|
const struct eprivate *e = exposable->private;
|
||||||
|
const struct particle *p = exposable->particle;
|
||||||
|
|
||||||
|
int target_size = p->icon_size;
|
||||||
|
double baseline = (double)y + (double)(height - target_size) / 2.0;
|
||||||
|
|
||||||
|
if (e->exposable != NULL) {
|
||||||
|
assert(e->icon.type == ICON_NONE);
|
||||||
|
e->exposable->expose(e->exposable, pix, x, y, height);
|
||||||
|
} else {
|
||||||
|
render_icon(&e->icon, x, baseline, target_size, pix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct exposable *
|
||||||
|
instantiate(const struct particle *particle, const struct tag_set *tags)
|
||||||
|
{
|
||||||
|
struct private *p = (struct private *)particle->private;
|
||||||
|
struct eprivate *e = calloc(1, sizeof(*e));
|
||||||
|
|
||||||
|
char *name = tags_expand_template(p->text, tags);
|
||||||
|
e->icon.type = ICON_NONE;
|
||||||
|
e->exposable = NULL;
|
||||||
|
char *icon_path = NULL;
|
||||||
|
if (strlen(name) == 0) {
|
||||||
|
LOG_WARN("No icon name/tag available");
|
||||||
|
goto fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: An icon cache
|
||||||
|
if (p->use_tag) {
|
||||||
|
const struct icon_tag *tag = icon_tag_for_name(tags, name);
|
||||||
|
|
||||||
|
if (!tag) {
|
||||||
|
LOG_WARN("No icon tag for %s", name);
|
||||||
|
goto fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct icon_pixmaps *pixmaps = tag->pixmaps(tag);
|
||||||
|
icon_from_pixmaps(&e->icon, pixmaps);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int min_size = 0, max_size = 0;
|
||||||
|
icon_path = find_icon(particle->themes->themes, particle->basedirs->basedirs, name, particle->icon_size,
|
||||||
|
particle->icon_themes->strings, &min_size, &max_size);
|
||||||
|
if (icon_path) {
|
||||||
|
int len = strlen(icon_path);
|
||||||
|
if ((icon_path[len - 3] == 's' && icon_from_svg(&e->icon, icon_path))
|
||||||
|
|| (icon_path[len - 3] == 'p' && icon_from_png(&e->icon, icon_path))) {
|
||||||
|
LOG_DBG("Loaded %s", icon_path);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_WARN("Failed to load icon path, not png or svg: %s", icon_path);
|
||||||
|
goto fallback;
|
||||||
|
}
|
||||||
|
LOG_WARN("Icon not found: %s", name);
|
||||||
|
|
||||||
|
fallback:
|
||||||
|
if (p->fallback) {
|
||||||
|
e->exposable = p->fallback->instantiate(p->fallback, tags);
|
||||||
|
assert(e->exposable != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(icon_path);
|
||||||
|
free(name);
|
||||||
|
|
||||||
|
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||||
|
exposable->private = e;
|
||||||
|
exposable->destroy = &exposable_destroy;
|
||||||
|
exposable->begin_expose = &begin_expose;
|
||||||
|
exposable->expose = &expose;
|
||||||
|
return exposable;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
particle_destroy(struct particle *particle)
|
||||||
|
{
|
||||||
|
struct private *p = particle->private;
|
||||||
|
if (p->fallback)
|
||||||
|
p->fallback->destroy(p->fallback);
|
||||||
|
free(p->text);
|
||||||
|
free(p);
|
||||||
|
particle_default_destroy(particle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct particle *
|
||||||
|
icon_new(struct particle *common, const char *name, bool use_tag, struct particle *fallback)
|
||||||
|
{
|
||||||
|
struct private *p = calloc(1, sizeof(*p));
|
||||||
|
p->text = strdup(name);
|
||||||
|
p->use_tag = use_tag;
|
||||||
|
p->fallback = fallback;
|
||||||
|
|
||||||
|
common->private = p;
|
||||||
|
common->destroy = &particle_destroy;
|
||||||
|
common->instantiate = &instantiate;
|
||||||
|
return common;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct particle *
|
||||||
|
from_conf(const struct yml_node *node, struct particle *common)
|
||||||
|
{
|
||||||
|
const struct yml_node *name = yml_get_value(node, "name");
|
||||||
|
const struct yml_node *use_tag_node = yml_get_value(node, "use-tag");
|
||||||
|
const struct yml_node *_fallback = yml_get_value(node, "fallback");
|
||||||
|
|
||||||
|
struct particle *fallback = NULL;
|
||||||
|
bool use_tag = false;
|
||||||
|
|
||||||
|
if (use_tag_node)
|
||||||
|
use_tag = yml_value_as_bool(use_tag_node);
|
||||||
|
if (_fallback)
|
||||||
|
fallback = conf_to_particle(_fallback, (struct conf_inherit){common->font, common->font_shaping, common->themes,
|
||||||
|
common->basedirs, common->icon_themes,
|
||||||
|
common->icon_size, common->foreground});
|
||||||
|
|
||||||
|
return icon_new(common, yml_value_as_string(name), use_tag, fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
verify_conf(keychain_t *chain, const struct yml_node *node)
|
||||||
|
{
|
||||||
|
static const struct attr_info attrs[] = {
|
||||||
|
{"name", true, &conf_verify_string},
|
||||||
|
{"use-tag", false, &conf_verify_bool},
|
||||||
|
{"fallback", false, &conf_verify_particle},
|
||||||
|
PARTICLE_COMMON_ATTRS,
|
||||||
|
};
|
||||||
|
|
||||||
|
return conf_verify_dict(chain, node, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct particle_iface particle_icon_iface = {
|
||||||
|
.verify_conf = &verify_conf,
|
||||||
|
.from_conf = &from_conf,
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES)
|
||||||
|
extern const struct particle_iface iface __attribute__((weak, alias("particle_icon_iface")));
|
||||||
|
#endif
|
|
@ -190,8 +190,9 @@ from_conf(const struct yml_node *node, struct particle *common)
|
||||||
|
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
for (struct yml_list_iter it = yml_list_iter(items); it.node != NULL; yml_list_next(&it), idx++) {
|
for (struct yml_list_iter it = yml_list_iter(items); it.node != NULL; yml_list_next(&it), idx++) {
|
||||||
parts[idx]
|
parts[idx] = conf_to_particle(it.node, (struct conf_inherit){common->font, common->font_shaping, common->themes,
|
||||||
= conf_to_particle(it.node, (struct conf_inherit){common->font, common->font_shaping, common->foreground});
|
common->basedirs, common->icon_themes,
|
||||||
|
common->icon_size, common->foreground});
|
||||||
}
|
}
|
||||||
|
|
||||||
return particle_list_new(common, parts, count, left_spacing, right_spacing);
|
return particle_list_new(common, parts, count, left_spacing, right_spacing);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include "tag.h"
|
||||||
|
#include "yml.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -85,6 +87,11 @@ str_condition(const char *tag_value, const char *cond_value, enum map_op op)
|
||||||
static bool
|
static bool
|
||||||
eval_comparison(const struct map_condition *map_cond, const struct tag_set *tags)
|
eval_comparison(const struct map_condition *map_cond, const struct tag_set *tags)
|
||||||
{
|
{
|
||||||
|
if (map_cond->op == MAP_OP_ICON_TAG) {
|
||||||
|
// Do the fallback check if MAP_OP_SELF for if an icon tag exists.
|
||||||
|
return icon_tag_for_name(tags, map_cond->tag) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
const struct tag *tag = tag_for_name(tags, map_cond->tag);
|
const struct tag *tag = tag_for_name(tags, map_cond->tag);
|
||||||
if (tag == NULL) {
|
if (tag == NULL) {
|
||||||
LOG_WARN("tag %s not found", map_cond->tag);
|
LOG_WARN("tag %s not found", map_cond->tag);
|
||||||
|
@ -170,6 +177,7 @@ free_map_condition(struct map_condition *c)
|
||||||
free(c->value);
|
free(c->value);
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
case MAP_OP_SELF:
|
case MAP_OP_SELF:
|
||||||
|
case MAP_OP_ICON_TAG:
|
||||||
free(c->tag);
|
free(c->tag);
|
||||||
break;
|
break;
|
||||||
case MAP_OP_AND:
|
case MAP_OP_AND:
|
||||||
|
@ -374,8 +382,15 @@ from_conf(const struct yml_node *node, struct particle *common)
|
||||||
|
|
||||||
struct particle_map particle_map[yml_dict_length(conditions)];
|
struct particle_map particle_map[yml_dict_length(conditions)];
|
||||||
|
|
||||||
struct conf_inherit inherited
|
struct conf_inherit inherited = {
|
||||||
= {.font = common->font, .font_shaping = common->font_shaping, .foreground = common->foreground};
|
.font = common->font,
|
||||||
|
.font_shaping = common->font_shaping,
|
||||||
|
.foreground = common->foreground,
|
||||||
|
.basedirs = common->basedirs,
|
||||||
|
.themes = common->themes,
|
||||||
|
.icon_themes = common->icon_themes,
|
||||||
|
.icon_size = common->icon_size,
|
||||||
|
};
|
||||||
|
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
for (struct yml_dict_iter it = yml_dict_iter(conditions); it.key != NULL; yml_dict_next(&it), idx++) {
|
for (struct yml_dict_iter it = yml_dict_iter(conditions); it.key != NULL; yml_dict_next(&it), idx++) {
|
||||||
|
|
|
@ -8,6 +8,7 @@ enum map_op {
|
||||||
MAP_OP_GE,
|
MAP_OP_GE,
|
||||||
MAP_OP_GT,
|
MAP_OP_GT,
|
||||||
MAP_OP_SELF,
|
MAP_OP_SELF,
|
||||||
|
MAP_OP_ICON_TAG,
|
||||||
MAP_OP_NOT,
|
MAP_OP_NOT,
|
||||||
|
|
||||||
MAP_OP_AND,
|
MAP_OP_AND,
|
||||||
|
|
|
@ -72,6 +72,7 @@ void yyerror(const char *s);
|
||||||
&& yylval.op = MAP_OP_AND; return BOOL_OP;
|
&& yylval.op = MAP_OP_AND; return BOOL_OP;
|
||||||
\|\| yylval.op = MAP_OP_OR; return BOOL_OP;
|
\|\| yylval.op = MAP_OP_OR; return BOOL_OP;
|
||||||
~ return NOT;
|
~ return NOT;
|
||||||
|
\+ return AT;
|
||||||
\( return L_PAR;
|
\( return L_PAR;
|
||||||
\) return R_PAR;
|
\) return R_PAR;
|
||||||
[ \t\n] ;
|
[ \t\n] ;
|
||||||
|
|
|
@ -21,7 +21,7 @@ void yyerror(const char *str);
|
||||||
enum map_op op;
|
enum map_op op;
|
||||||
}
|
}
|
||||||
|
|
||||||
%token WORD STRING CMP_OP L_PAR R_PAR
|
%token WORD STRING CMP_OP L_PAR R_PAR AT
|
||||||
%left BOOL_OP
|
%left BOOL_OP
|
||||||
%precedence NOT
|
%precedence NOT
|
||||||
|
|
||||||
|
@ -33,6 +33,12 @@ void yyerror(const char *str);
|
||||||
result: condition { MAP_CONDITION_PARSE_RESULT = $<condition>1; };
|
result: condition { MAP_CONDITION_PARSE_RESULT = $<condition>1; };
|
||||||
|
|
||||||
condition:
|
condition:
|
||||||
|
AT WORD {
|
||||||
|
$<condition>$ = malloc(sizeof(struct map_condition));
|
||||||
|
$<condition>$->tag = $<str>2;
|
||||||
|
$<condition>$->op = MAP_OP_ICON_TAG;
|
||||||
|
}
|
||||||
|
|
|
||||||
WORD {
|
WORD {
|
||||||
$<condition>$ = malloc(sizeof(struct map_condition));
|
$<condition>$ = malloc(sizeof(struct map_condition));
|
||||||
$<condition>$->tag = $<str>1;
|
$<condition>$->tag = $<str>1;
|
||||||
|
@ -84,6 +90,7 @@ token_to_str(yysymbol_kind_t tkn)
|
||||||
case YYSYMBOL_L_PAR: return "(";
|
case YYSYMBOL_L_PAR: return "(";
|
||||||
case YYSYMBOL_R_PAR: return ")";
|
case YYSYMBOL_R_PAR: return ")";
|
||||||
case YYSYMBOL_NOT: return "~";
|
case YYSYMBOL_NOT: return "~";
|
||||||
|
case YYSYMBOL_AT: return "+";
|
||||||
default: return yysymbol_name(tkn);
|
default: return yysymbol_name(tkn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ pfiles = pgen.process('map.y')
|
||||||
|
|
||||||
map_parser = declare_dependency(sources: [pfiles, lfiles], include_directories: '.')
|
map_parser = declare_dependency(sources: [pfiles, lfiles], include_directories: '.')
|
||||||
|
|
||||||
particle_sdk = declare_dependency(dependencies: [pixman, tllist, fcft])
|
particle_sdk = declare_dependency(dependencies: [pixman, tllist, fcft, nanosvg])
|
||||||
|
|
||||||
dynlist_lib = build_target(
|
dynlist_lib = build_target(
|
||||||
'dynlist', 'dynlist.c', 'dynlist.h', dependencies: particle_sdk,
|
'dynlist', 'dynlist.c', 'dynlist.h', dependencies: particle_sdk,
|
||||||
|
@ -40,6 +40,7 @@ deps = {
|
||||||
'progress-bar': [],
|
'progress-bar': [],
|
||||||
'ramp': [],
|
'ramp': [],
|
||||||
'string': [],
|
'string': [],
|
||||||
|
'icon': [],
|
||||||
}
|
}
|
||||||
|
|
||||||
particles = []
|
particles = []
|
||||||
|
|
|
@ -296,6 +296,10 @@ from_conf(const struct yml_node *node, struct particle *common)
|
||||||
.font = common->font,
|
.font = common->font,
|
||||||
.font_shaping = common->font_shaping,
|
.font_shaping = common->font_shaping,
|
||||||
.foreground = common->foreground,
|
.foreground = common->foreground,
|
||||||
|
.basedirs = common->basedirs,
|
||||||
|
.themes = common->themes,
|
||||||
|
.icon_themes = common->icon_themes,
|
||||||
|
.icon_size = common->icon_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
return progress_bar_new(common, yml_value_as_string(tag), yml_value_as_int(length),
|
return progress_bar_new(common, yml_value_as_string(tag), yml_value_as_int(length),
|
||||||
|
|
|
@ -199,8 +199,9 @@ from_conf(const struct yml_node *node, struct particle *common)
|
||||||
|
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
for (struct yml_list_iter it = yml_list_iter(items); it.node != NULL; yml_list_next(&it), idx++) {
|
for (struct yml_list_iter it = yml_list_iter(items); it.node != NULL; yml_list_next(&it), idx++) {
|
||||||
parts[idx]
|
parts[idx] = conf_to_particle(it.node, (struct conf_inherit){common->font, common->font_shaping, common->themes,
|
||||||
= conf_to_particle(it.node, (struct conf_inherit){common->font, common->font_shaping, common->foreground});
|
common->basedirs, common->icon_themes,
|
||||||
|
common->icon_size, common->foreground});
|
||||||
}
|
}
|
||||||
|
|
||||||
long min_v = min != NULL ? yml_value_as_int(min) : 0;
|
long min_v = min != NULL ? yml_value_as_int(min) : 0;
|
||||||
|
|
2
plugin.c
2
plugin.c
|
@ -97,6 +97,7 @@ EXTERN_PARTICLE(map);
|
||||||
EXTERN_PARTICLE(progress_bar);
|
EXTERN_PARTICLE(progress_bar);
|
||||||
EXTERN_PARTICLE(ramp);
|
EXTERN_PARTICLE(ramp);
|
||||||
EXTERN_PARTICLE(string);
|
EXTERN_PARTICLE(string);
|
||||||
|
EXTERN_PARTICLE(icon);
|
||||||
|
|
||||||
EXTERN_DECORATION(background);
|
EXTERN_DECORATION(background);
|
||||||
EXTERN_DECORATION(border);
|
EXTERN_DECORATION(border);
|
||||||
|
@ -227,6 +228,7 @@ static void __attribute__((constructor)) init(void)
|
||||||
REGISTER_CORE_PARTICLE(progress-bar, progress_bar);
|
REGISTER_CORE_PARTICLE(progress-bar, progress_bar);
|
||||||
REGISTER_CORE_PARTICLE(ramp, ramp);
|
REGISTER_CORE_PARTICLE(ramp, ramp);
|
||||||
REGISTER_CORE_PARTICLE(string, string);
|
REGISTER_CORE_PARTICLE(string, string);
|
||||||
|
REGISTER_CORE_PARTICLE(icon, icon);
|
||||||
|
|
||||||
REGISTER_CORE_DECORATION(background, background);
|
REGISTER_CORE_DECORATION(background, background);
|
||||||
REGISTER_CORE_DECORATION(border, border);
|
REGISTER_CORE_DECORATION(border, border);
|
||||||
|
|
5
png-yambar.h
Normal file
5
png-yambar.h
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pixman.h>
|
||||||
|
|
||||||
|
pixman_image_t *png_load(const char *path);
|
171
png.c
Normal file
171
png.c
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
#include "png-yambar.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <pixman.h>
|
||||||
|
#include <png.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "png"
|
||||||
|
#define LOG_ENABLE_DBG 0
|
||||||
|
#include "log.h"
|
||||||
|
#include "stride.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
png_warning_cb(png_structp png_ptr, png_const_charp warning_msg)
|
||||||
|
{
|
||||||
|
LOG_WARN("libpng: %s", warning_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
pixman_image_t *
|
||||||
|
png_load(const char *path)
|
||||||
|
{
|
||||||
|
pixman_image_t *pix = NULL;
|
||||||
|
|
||||||
|
FILE *fp = NULL;
|
||||||
|
png_structp png_ptr = NULL;
|
||||||
|
png_infop info_ptr = NULL;
|
||||||
|
png_bytepp row_pointers = NULL;
|
||||||
|
uint8_t *image_data = NULL;
|
||||||
|
|
||||||
|
/* open file and test for it being a png */
|
||||||
|
if ((fp = fopen(path, "rb")) == NULL) {
|
||||||
|
// LOG_ERRNO("%s: failed to open", path);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify PNG header */
|
||||||
|
uint8_t header[8] = {0};
|
||||||
|
if (fread(header, 1, 8, fp) != 8 || png_sig_cmp(header, 0, 8)) {
|
||||||
|
// LOG_ERR("%s: not a PNG", path);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare for reading the PNG */
|
||||||
|
if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL
|
||||||
|
|| (info_ptr = png_create_info_struct(png_ptr)) == NULL) {
|
||||||
|
LOG_ERR("%s: failed to initialize libpng", path);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||||
|
LOG_ERR("%s: libpng error", path);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set custom “warning” function */
|
||||||
|
png_set_error_fn(png_ptr, png_get_error_ptr(png_ptr), NULL, &png_warning_cb);
|
||||||
|
|
||||||
|
png_init_io(png_ptr, fp);
|
||||||
|
png_set_sig_bytes(png_ptr, 8);
|
||||||
|
|
||||||
|
/* Get meta data */
|
||||||
|
png_read_info(png_ptr, info_ptr);
|
||||||
|
int width = png_get_image_width(png_ptr, info_ptr);
|
||||||
|
int height = png_get_image_height(png_ptr, info_ptr);
|
||||||
|
png_byte color_type = png_get_color_type(png_ptr, info_ptr);
|
||||||
|
png_byte bit_depth __attribute__((unused)) = png_get_bit_depth(png_ptr, info_ptr);
|
||||||
|
int channels __attribute__((unused)) = png_get_channels(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
LOG_DBG("%s: %dx%d@%hhubpp, %d channels", path, width, height, bit_depth, channels);
|
||||||
|
|
||||||
|
png_set_packing(png_ptr);
|
||||||
|
png_set_interlace_handling(png_ptr);
|
||||||
|
png_set_strip_16(png_ptr); /* "pack" 16-bit colors to 8-bit */
|
||||||
|
png_set_bgr(png_ptr);
|
||||||
|
|
||||||
|
/* pixman expects pre-multiplied alpha */
|
||||||
|
|
||||||
|
/* Tell libpng to expand to RGB(A) when necessary, and tell pixman
|
||||||
|
* whether we have alpha or not */
|
||||||
|
pixman_format_code_t format;
|
||||||
|
switch (color_type) {
|
||||||
|
case PNG_COLOR_TYPE_GRAY:
|
||||||
|
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
||||||
|
LOG_DBG("%d-bit gray%s", bit_depth, color_type == PNG_COLOR_TYPE_GRAY_ALPHA ? "+alpha" : "");
|
||||||
|
|
||||||
|
if (bit_depth < 8)
|
||||||
|
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
||||||
|
|
||||||
|
png_set_gray_to_rgb(png_ptr);
|
||||||
|
format = color_type == PNG_COLOR_TYPE_GRAY ? PIXMAN_r8g8b8 : PIXMAN_a8r8g8b8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PNG_COLOR_TYPE_PALETTE:
|
||||||
|
LOG_DBG("%d-bit colormap%s", bit_depth, png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? "+tRNS" : "");
|
||||||
|
|
||||||
|
png_set_palette_to_rgb(png_ptr);
|
||||||
|
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
|
||||||
|
png_set_tRNS_to_alpha(png_ptr);
|
||||||
|
format = PIXMAN_a8r8g8b8;
|
||||||
|
} else
|
||||||
|
format = PIXMAN_r8g8b8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PNG_COLOR_TYPE_RGB:
|
||||||
|
LOG_DBG("RGB");
|
||||||
|
format = PIXMAN_r8g8b8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PNG_COLOR_TYPE_RGBA:
|
||||||
|
LOG_DBG("RGBA");
|
||||||
|
format = PIXMAN_a8r8g8b8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_read_update_info(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
size_t row_bytes __attribute__((unused)) = png_get_rowbytes(png_ptr, info_ptr);
|
||||||
|
int stride = stride_for_format_and_width(format, width);
|
||||||
|
image_data = malloc(height * stride);
|
||||||
|
|
||||||
|
LOG_DBG("stride=%d, row-bytes=%zu", stride, row_bytes);
|
||||||
|
assert(stride >= row_bytes);
|
||||||
|
|
||||||
|
row_pointers = malloc(height * sizeof(png_bytep));
|
||||||
|
for (int i = 0; i < height; i++)
|
||||||
|
row_pointers[i] = &image_data[i * stride];
|
||||||
|
|
||||||
|
png_read_image(png_ptr, row_pointers);
|
||||||
|
|
||||||
|
/* pixman expects pre-multiplied alpha */
|
||||||
|
if (format == PIXMAN_a8r8g8b8) {
|
||||||
|
for (int i = 0; i < height; i++) {
|
||||||
|
uint32_t *p = (uint32_t *)row_pointers[i];
|
||||||
|
for (int j = 0; j < width; j++, p++) {
|
||||||
|
uint8_t a = (*p >> 24) & 0xff;
|
||||||
|
uint8_t r = (*p >> 16) & 0xff;
|
||||||
|
uint8_t g = (*p >> 8) & 0xff;
|
||||||
|
uint8_t b = (*p >> 0) & 0xff;
|
||||||
|
|
||||||
|
if (a == 0xff)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (a == 0) {
|
||||||
|
r = g = b = 0;
|
||||||
|
} else {
|
||||||
|
r = r * a / 0xff;
|
||||||
|
g = g * a / 0xff;
|
||||||
|
b = b * a / 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = (uint32_t)a << 24 | r << 16 | g << 8 | b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pix = pixman_image_create_bits_no_clear(format, width, height, (uint32_t *)image_data, stride);
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (pix == NULL)
|
||||||
|
free(image_data);
|
||||||
|
free(row_pointers);
|
||||||
|
if (png_ptr != NULL)
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||||
|
if (fp != NULL)
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return pix;
|
||||||
|
}
|
42
stringop.c
Normal file
42
stringop.c
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "icon"
|
||||||
|
#define LOG_ENABLE_DBG 0
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
char *
|
||||||
|
vformat_str(const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
char *str = NULL;
|
||||||
|
va_list args_copy;
|
||||||
|
va_copy(args_copy, args);
|
||||||
|
|
||||||
|
int len = vsnprintf(NULL, 0, fmt, args);
|
||||||
|
if (len < 0) {
|
||||||
|
LOG_ERR("vsnprintf(\"%s\") failed", fmt);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
str = malloc(len + 1);
|
||||||
|
if (str == NULL) {
|
||||||
|
LOG_ERR("malloc() failed");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
vsnprintf(str, len + 1, fmt, args_copy);
|
||||||
|
|
||||||
|
out:
|
||||||
|
va_end(args_copy);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
format_str(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
char *str = vformat_str(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return str;
|
||||||
|
}
|
12
stringop.h
Normal file
12
stringop.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define _SWAY_ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end)))
|
||||||
|
#else
|
||||||
|
#define _SWAY_ATTRIB_PRINTF(start, end)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *vformat_str(const char *fmt, va_list args) _SWAY_ATTRIB_PRINTF(1, 0);
|
||||||
|
char *format_str(const char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2);
|
61
svg.c
Normal file
61
svg.c
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#include "svg.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "svg"
|
||||||
|
#define LOG_ENABLE_DBG 0
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#include <nanosvg.h>
|
||||||
|
#include <nanosvgrast.h>
|
||||||
|
|
||||||
|
pixman_image_t *
|
||||||
|
svg_load(const char *path, int size)
|
||||||
|
{
|
||||||
|
NSVGimage *svg = nsvgParseFromFile(path, "px", 96);
|
||||||
|
if (svg == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (svg->width == 0 || svg->height == 0) {
|
||||||
|
nsvgDelete(svg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NSVGrasterizer *rast = nsvgCreateRasterizer();
|
||||||
|
|
||||||
|
const int w = size;
|
||||||
|
const int h = size;
|
||||||
|
float scale = w > h ? w / svg->width : h / svg->height;
|
||||||
|
|
||||||
|
uint8_t *data = malloc(h * w * 4);
|
||||||
|
nsvgRasterize(rast, svg, 0, 0, scale, data, w, h, w * 4);
|
||||||
|
|
||||||
|
nsvgDeleteRasterizer(rast);
|
||||||
|
nsvgDelete(svg);
|
||||||
|
|
||||||
|
pixman_image_t *img = pixman_image_create_bits_no_clear(PIXMAN_a8b8g8r8, w, h, (uint32_t *)data, w * 4);
|
||||||
|
|
||||||
|
/* Nanosvg produces non-premultiplied ABGR, while pixman expects
|
||||||
|
* premultiplied */
|
||||||
|
for (uint32_t *abgr = (uint32_t *)data; abgr < (uint32_t *)(data + h * w * 4); abgr++) {
|
||||||
|
uint8_t alpha = (*abgr >> 24) & 0xff;
|
||||||
|
uint8_t blue = (*abgr >> 16) & 0xff;
|
||||||
|
uint8_t green = (*abgr >> 8) & 0xff;
|
||||||
|
uint8_t red = (*abgr >> 0) & 0xff;
|
||||||
|
|
||||||
|
if (alpha == 0xff)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (alpha == 0x00)
|
||||||
|
blue = green = red = 0x00;
|
||||||
|
else {
|
||||||
|
blue = blue * alpha / 0xff;
|
||||||
|
green = green * alpha / 0xff;
|
||||||
|
red = red * alpha / 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
*abgr = (uint32_t)alpha << 24 | blue << 16 | green << 8 | red;
|
||||||
|
}
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
5
svg.h
Normal file
5
svg.h
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pixman.h>
|
||||||
|
|
||||||
|
pixman_image_t *svg_load(const char *path, int size);
|
88
tag.c
88
tag.c
|
@ -9,7 +9,8 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#define LOG_MODULE "tag"
|
#define LOG_MODULE "tag"
|
||||||
#define LOG_ENABLE_DBG 1
|
#define LOG_ENABLE_DBG 0
|
||||||
|
#include "icon.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
|
|
||||||
|
@ -29,6 +30,11 @@ struct private
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct icon_private {
|
||||||
|
char *name;
|
||||||
|
struct icon_pixmaps *pixmaps;
|
||||||
|
};
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
tag_name(const struct tag *tag)
|
tag_name(const struct tag *tag)
|
||||||
{
|
{
|
||||||
|
@ -375,6 +381,50 @@ tag_new_string(struct module *owner, const char *name, const char *value)
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
icon_tag_name(const struct icon_tag *icon_tag)
|
||||||
|
{
|
||||||
|
const struct icon_private *priv = icon_tag->private;
|
||||||
|
return priv->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct icon_pixmaps *
|
||||||
|
pixmaps_as_pixmaps(const struct icon_tag *icon_tag)
|
||||||
|
{
|
||||||
|
const struct icon_private *priv = icon_tag->private;
|
||||||
|
return priv->pixmaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pixmap_destroy(struct icon_tag *icon_tag)
|
||||||
|
{
|
||||||
|
struct icon_private *priv = icon_tag->private;
|
||||||
|
icon_pixmaps_dec(priv->pixmaps);
|
||||||
|
free(priv->name);
|
||||||
|
free(priv);
|
||||||
|
free(icon_tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct icon_tag *
|
||||||
|
icon_tag_new_pixmap(struct module *owner, const char *name, struct icon_pixmaps *pixmaps)
|
||||||
|
{
|
||||||
|
if (pixmaps == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
struct icon_private *priv = malloc(sizeof(*priv));
|
||||||
|
priv->name = strdup(name);
|
||||||
|
priv->pixmaps = icon_pixmaps_inc(pixmaps);
|
||||||
|
|
||||||
|
struct icon_tag *icon_tag = malloc(sizeof(*icon_tag));
|
||||||
|
icon_tag->private = priv;
|
||||||
|
icon_tag->owner = owner;
|
||||||
|
icon_tag->name = &icon_tag_name;
|
||||||
|
icon_tag->pixmaps = &pixmaps_as_pixmaps;
|
||||||
|
icon_tag->destroy = &pixmap_destroy;
|
||||||
|
|
||||||
|
return icon_tag;
|
||||||
|
}
|
||||||
|
|
||||||
const struct tag *
|
const struct tag *
|
||||||
tag_for_name(const struct tag_set *set, const char *name)
|
tag_for_name(const struct tag_set *set, const char *name)
|
||||||
{
|
{
|
||||||
|
@ -390,14 +440,22 @@ tag_for_name(const struct tag_set *set, const char *name)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
const struct icon_tag *
|
||||||
tag_set_destroy(struct tag_set *set)
|
icon_tag_for_name(const struct tag_set *set, const char *name)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < set->count; i++)
|
if (set == NULL)
|
||||||
set->tags[i]->destroy(set->tags[i]);
|
return NULL;
|
||||||
|
|
||||||
set->tags = NULL;
|
for (size_t i = 0; i < set->icon_count; i++) {
|
||||||
set->count = 0;
|
const struct icon_tag *tag = set->icon_tags[i];
|
||||||
|
if (!tag)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strcmp(tag->name(tag), name) == 0)
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sbuf {
|
struct sbuf {
|
||||||
|
@ -744,3 +802,19 @@ tags_expand_templates(char *expanded[], const char *template[], size_t nmemb, co
|
||||||
for (size_t i = 0; i < nmemb; i++)
|
for (size_t i = 0; i < nmemb; i++)
|
||||||
expanded[i] = tags_expand_template(template[i], tags);
|
expanded[i] = tags_expand_template(template[i], tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tag_set_destroy(struct tag_set *set)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < set->count; i++)
|
||||||
|
set->tags[i]->destroy(set->tags[i]);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < set->icon_count; i++) {
|
||||||
|
if (set->icon_tags[i] != NULL) {
|
||||||
|
set->icon_tags[i]->destroy(set->icon_tags[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set->tags = NULL;
|
||||||
|
set->count = 0;
|
||||||
|
}
|
||||||
|
|
17
tag.h
17
tag.h
|
@ -1,8 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <pixman.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "icon.h"
|
||||||
|
|
||||||
enum tag_type {
|
enum tag_type {
|
||||||
TAG_TYPE_BOOL,
|
TAG_TYPE_BOOL,
|
||||||
TAG_TYPE_INT,
|
TAG_TYPE_INT,
|
||||||
|
@ -37,9 +40,20 @@ struct tag {
|
||||||
bool (*refresh_in)(const struct tag *tag, long units);
|
bool (*refresh_in)(const struct tag *tag, long units);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct icon_tag {
|
||||||
|
void *private;
|
||||||
|
struct module *owner;
|
||||||
|
|
||||||
|
void (*destroy)(struct icon_tag *destroy);
|
||||||
|
const char *(*name)(const struct icon_tag *tag);
|
||||||
|
struct icon_pixmaps *(*pixmaps)(const struct icon_tag *tag);
|
||||||
|
};
|
||||||
|
|
||||||
struct tag_set {
|
struct tag_set {
|
||||||
struct tag **tags;
|
struct tag **tags;
|
||||||
|
struct icon_tag **icon_tags;
|
||||||
size_t count;
|
size_t count;
|
||||||
|
size_t icon_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tag *tag_new_int(struct module *owner, const char *name, long value);
|
struct tag *tag_new_int(struct module *owner, const char *name, long value);
|
||||||
|
@ -50,7 +64,10 @@ struct tag *tag_new_bool(struct module *owner, const char *name, bool value);
|
||||||
struct tag *tag_new_float(struct module *owner, const char *name, double value);
|
struct tag *tag_new_float(struct module *owner, const char *name, double value);
|
||||||
struct tag *tag_new_string(struct module *owner, const char *name, const char *value);
|
struct tag *tag_new_string(struct module *owner, const char *name, const char *value);
|
||||||
|
|
||||||
|
struct icon_tag *icon_tag_new_pixmap(struct module *owner, const char *name, struct icon_pixmaps *icon_pixmap);
|
||||||
|
|
||||||
const struct tag *tag_for_name(const struct tag_set *set, const char *name);
|
const struct tag *tag_for_name(const struct tag_set *set, const char *name);
|
||||||
|
const struct icon_tag *icon_tag_for_name(const struct tag_set *set, const char *name);
|
||||||
void tag_set_destroy(struct tag_set *set);
|
void tag_set_destroy(struct tag_set *set);
|
||||||
|
|
||||||
/* Utility functions */
|
/* Utility functions */
|
||||||
|
|
0
xdg.c
Normal file
0
xdg.c
Normal file
Loading…
Add table
Reference in a new issue