GCC Code Coverage Report


Directory: ./
File: channeloutput/X11Matrix.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 0 123 0.0%
Functions: 0 10 0.0%
Branches: 0 104 0.0%

Line Branch Exec Source
1 /*
2 * This file is part of the Falcon Player (FPP) and is Copyright (C)
3 * 2013-2022 by the Falcon Player Developers.
4 *
5 * The Falcon Player (FPP) is free software, and is covered under
6 * multiple Open Source licenses. Please see the included 'LICENSES'
7 * file for descriptions of what files are covered by each license.
8 *
9 * This source file is covered under the GPL v2 as described in the
10 * included LICENSE.GPL file.
11 */
12
13 #include "fpp-pch.h"
14
15 #include <climits>
16
17 #include "fpp-json.h"
18
19 #include <X11/Xutil.h>
20
21 #include "../log.h"
22
23 #include "X11Matrix.h"
24 #include "serialutil.h"
25
26 #include "Plugin.h"
27 class X11MatrixPlugin : public FPPPlugins::Plugin, public FPPPlugins::ChannelOutputPlugin {
28 public:
29 ✗ X11MatrixPlugin() :
30 ✗ FPPPlugins::Plugin("X11Matrix") {
31 ✗ }
32 ✗ virtual ChannelOutput* createChannelOutput(unsigned int startChannel, unsigned int channelCount) override {
33 ✗ return new X11MatrixOutput(startChannel, channelCount);
34 }
35 };
36
37 extern "C" {
38 ✗ FPPPlugins::Plugin* createPlugin() {
39 ✗ return new X11MatrixPlugin();
40 }
41 }
42
43 /////////////////////////////////////////////////////////////////////////////
44
45 /*
46 *
47 */
48 ✗ X11MatrixOutput::X11MatrixOutput(unsigned int startChannel,
49 ✗ unsigned int channelCount) :
50 ThreadedChannelOutput(startChannel, channelCount),
51 ✗ m_width(0),
52 ✗ m_height(0),
53 ✗ m_scale(10),
54 ✗ m_scaleWidth(0),
55 ✗ m_scaleHeight(0),
56 ✗ m_imageData(NULL) {
57 ✗ LogDebug(VB_CHANNELOUT, "X11MatrixOutput::X11MatrixOutput(%u, %u)\n",
58 startChannel, channelCount);
59
60 ✗ XInitThreads();
61
62 ✗ m_useDoubleBuffer = 1;
63 ✗ }
64
65 /*
66 *
67 */
68 ✗ X11MatrixOutput::~X11MatrixOutput() {
69 ✗ LogDebug(VB_CHANNELOUT, "X11MatrixOutput::~X11MatrixOutput()\n");
70 ✗ }
71
72 /*
73 *
74 */
75 ✗ int X11MatrixOutput::Init(Json::Value config) {
76 ✗ LogDebug(VB_CHANNELOUT, "X11MatrixOutput::Init(JSON)\n");
77
78 ✗ m_width = config["width"].asInt();
79 ✗ m_height = config["height"].asInt();
80 ✗ m_scale = config["scale"].asInt();
81
82 ✗ if (config.isMember("title"))
83 ✗ m_title = config["title"].asString();
84 else
85 ✗ m_title = "X11Matrix";
86
87 ✗ if (!m_scale)
88 ✗ m_scale = 10;
89
90 // RawSendData() reads width * height * 3 channels and scales them into
91 // the image: a matrix larger than the output's channels over-read the
92 // channel data, and a large scale overflowed the image size.
93 ✗ if (m_width < 1 || m_height < 1 || m_scale < 1 || m_scale > 100 ||
94 ✗ (int64_t)m_width * m_height * 3 > m_channelCount ||
95 ✗ (int64_t)m_width * m_height * m_scale * m_scale * 4 > (int64_t)INT_MAX) {
96 ✗ LogErr(VB_CHANNELOUT, "X11Matrix: invalid size %dx%d at scale %d for %u channels\n",
97 m_width, m_height, m_scale, m_channelCount);
98 ✗ return 0;
99 }
100
101 ✗ m_scaleWidth = m_scale * m_width;
102 ✗ m_scaleHeight = m_scale * m_height;
103 ✗ m_imageData = (char*)calloc(m_scaleWidth * m_scaleHeight * 4, 1);
104
105 // Initialize X11 Window here
106 ✗ const char* dsp = getenv("DISPLAY");
107 ✗ if (dsp == nullptr) {
108 ✗ dsp = ":0";
109 }
110 ✗ m_display = XOpenDisplay(dsp);
111 ✗ if (!m_display) {
112 ✗ LogErr(VB_CHANNELOUT, "Unable to connect to X Server: %s\n", dsp);
113 ✗ return 0;
114 }
115
116 ✗ m_screen = DefaultScreen(m_display);
117
118 ✗ m_image = XCreateImage(m_display, CopyFromParent, 24, ZPixmap, 0,
119 ✗ m_imageData, m_scaleWidth, m_scaleHeight, 32, m_scaleWidth * 4);
120
121 ✗ int win_x = 100;
122 ✗ int win_y = 100;
123 XSetWindowAttributes attributes;
124
125 ✗ attributes.background_pixel = BlackPixel(m_display, m_screen);
126
127 XGCValues values;
128
129 ✗ m_pixmap = XCreatePixmap(m_display, XDefaultRootWindow(m_display),
130 ✗ m_scaleWidth, m_scaleHeight, 24);
131
132 ✗ m_gc = XCreateGC(m_display, m_pixmap, 0, &values);
133 ✗ int32_t tgc = reinterpret_cast<uintptr_t>(m_gc);
134 ✗ if (tgc < 0) {
135 ✗ LogErr(VB_CHANNELOUT, "Unable to create GC\n");
136 ✗ return 0;
137 }
138
139 ✗ m_window = XCreateWindow(
140 ✗ m_display, RootWindow(m_display, m_screen), win_x, win_y,
141 ✗ m_scaleWidth, m_scaleHeight, 5, 24, InputOutput,
142 ✗ DefaultVisual(m_display, m_screen), CWBackPixel, &attributes);
143
144 ✗ XMapWindow(m_display, m_window);
145
146 ✗ XStoreName(m_display, m_window, m_title.c_str());
147 ✗ XSetIconName(m_display, m_window, m_title.c_str());
148
149 ✗ XFlush(m_display);
150
151 ✗ return ThreadedChannelOutput::Init(config);
152 }
153
154 /*
155 *
156 */
157 ✗ int X11MatrixOutput::Close(void) {
158 ✗ LogDebug(VB_CHANNELOUT, "X11MatrixOutput::Close()\n");
159
160 // Close X11 Window here
161
162 ✗ if (m_display) {
163 ✗ XLockDisplay(m_display);
164 ✗ XDestroyWindow(m_display, m_window);
165 ✗ XFreePixmap(m_display, m_pixmap);
166 ✗ XFreeGC(m_display, m_gc);
167 ✗ XDestroyImage(m_image);
168 ✗ XUnlockDisplay(m_display);
169 ✗ XCloseDisplay(m_display);
170 }
171 ✗ m_display = nullptr;
172
173 ✗ return ThreadedChannelOutput::Close();
174 }
175
176 /*
177 *
178 */
179 ✗ int X11MatrixOutput::RawSendData(unsigned char* channelData) {
180 ✗ LogExcess(VB_CHANNELOUT, "X11MatrixOutput::RawSendData(%p)\n", channelData);
181
182 // Draw on X11 Window here
183 ✗ char* c = (char*)channelData;
184 ✗ char* d = m_imageData;
185 ✗ char r = 0;
186 ✗ char g = 0;
187 ✗ char b = 0;
188 ✗ char* row = NULL;
189 ✗ int stride = m_scaleWidth * 4;
190 ✗ for (int y = 0; y < m_height; y++) {
191 ✗ row = d;
192
193 ✗ for (int x = 0; x < m_width; x++) {
194 ✗ r = *(c++);
195 ✗ g = *(c++);
196 ✗ b = *(c++);
197
198 ✗ for (int s = 0; s < m_scale; s++) {
199 ✗ *(d++) = b;
200 ✗ *(d++) = g;
201 ✗ *(d++) = r;
202 ✗ *(d++) = 0;
203 }
204 }
205
206 ✗ for (int s = 1; s < m_scale; s++) {
207 ✗ memcpy(row + stride, row, stride);
208 ✗ row += stride;
209 ✗ d += stride;
210 }
211 }
212
213 ✗ XLockDisplay(m_display);
214
215 ✗ XPutImage(m_display, m_window, m_gc, m_image, 0, 0, 0, 0, m_scaleWidth, m_scaleHeight);
216
217 ✗ XSync(m_display, True);
218 ✗ XFlush(m_display);
219
220 ✗ XUnlockDisplay(m_display);
221
222 ✗ return m_channelCount;
223 }
224
225 /*
226 *
227 */
228 ✗ void X11MatrixOutput::GetRequiredChannelRanges(const std::function<void(int, int)>& addRange) {
229 ✗ addRange(m_startChannel, m_startChannel + m_channelCount - 1);
230 ✗ }
231
232 /*
233 *
234 */
235 ✗ void X11MatrixOutput::DumpConfig(void) {
236 ✗ LogDebug(VB_CHANNELOUT, "X11MatrixOutput::DumpConfig()\n");
237
238 ✗ LogDebug(VB_CHANNELOUT, " Width : %d\n", m_width);
239 ✗ LogDebug(VB_CHANNELOUT, " Height : %d\n", m_height);
240 ✗ LogDebug(VB_CHANNELOUT, " Scale : %d\n", m_scale);
241 ✗ LogDebug(VB_CHANNELOUT, " Scaled Width : %d\n", m_scaleWidth);
242 ✗ LogDebug(VB_CHANNELOUT, " Scaled Height : %d\n", m_scaleHeight);
243
244 ✗ ThreadedChannelOutput::DumpConfig();
245 ✗ }
246