GCC Code Coverage Report


Directory: ./
File: fppmm.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 119 175 68.0%
Functions: 7 8 87.5%
Branches: 152 315 48.3%

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 "fpp-json.h"
16
17 #include <sys/stat.h>
18 #include <cstring>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string>
26 #include <unistd.h>
27
28 #include "common.h"
29 #include "commands/Commands.h"
30
31 #include "fppversion.h"
32
33 char* blockName = NULL;
34 char* inputFilename = NULL;
35 int isActive = -1;
36 char* channels = NULL;
37 int channelData = -1;
38 int displayVers = 0;
39
40 /*
41 * Usage information for fppmm binary
42 */
43 ✗ void usage(char* appname) {
44 ✗ printf("Usage: %s [OPTIONS]\n", appname);
45 ✗ printf("\n");
46 ✗ printf(" Options:\n");
47 ✗ printf(" -V - Print version information\n");
48 ✗ printf(" -c CHANNEL -s VALUE - Set channel number CHANNEL to VALUE\n");
49 ✗ printf(" VALUE of -1 will delete the range\n");
50 ✗ printf(" -m MODEL - List info about Pixel Overlay MODEL\n");
51 ✗ printf(" -m MODEL -o MODE - Set Pixel Overlay mode, Mode is one of:\n");
52 ✗ printf(" off, on, transparent, transparentrgb\n");
53 ✗ printf(" -m MODEL -f FILENAME - Copy raw FILENAME data to MODEL\n");
54 ✗ printf(" -m MODEL -s VALUE - Fill MODEL with VALUE for all channels\n");
55 ✗ printf(" -h - This help output\n");
56 ✗ }
57
58 /*
59 * Parse command line arguments for fppmm binary
60 */
61 12 static void parseArguments(int argc, char** argv) {
62 12 char* s = NULL;
63 int c;
64
65 while (1) {
66
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35 int this_option_optind = optind ? optind : 1;
67 35 int option_index = 0;
68 static struct option long_options[] = {
69 { "mapname", required_argument, 0, 'm' },
70 { "overlaymode", required_argument, 0, 'o' },
71 { "filename", required_argument, 0, 'f' },
72 { "displayvers", no_argument, 0, 'V' },
73 { "channel", required_argument, 0, 'c' },
74 { "setvalue", required_argument, 0, 's' },
75 { "help", no_argument, 0, 'h' },
76 { 0, 0, 0, 0 }
77 };
78
79 35 c = getopt_long(argc, argv, "m:o:f:t:c:s:hV", long_options, &option_index);
80
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 23 times.
35 if (c == -1)
81 12 break;
82
83
5/8
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
23 switch (c) {
84 ✗ case 'V':
85 ✗ printVersionInfo();
86 ✗ exit(0);
87 10 case 'm':
88 10 blockName = strdup(optarg);
89 10 break;
90 3 case 'o':
91
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!strcmp(optarg, "off"))
92 ✗ isActive = 0;
93
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 else if (!strcmp(optarg, "on"))
94 3 isActive = 1;
95 ✗ else if (!strcmp(optarg, "transparent"))
96 ✗ isActive = 2;
97 ✗ else if (!strcmp(optarg, "transparentrgb"))
98 ✗ isActive = 3;
99
100 3 break;
101 5 case 'f':
102 5 inputFilename = strdup(optarg);
103 5 break;
104 2 case 'c':
105 2 channels = strdup(optarg);
106 2 break;
107 3 case 's':
108 3 channelData = strtol(optarg, NULL, 10);
109 3 break;
110 ✗ case 'h':
111 ✗ usage(argv[0]);
112 ✗ exit(EXIT_SUCCESS);
113 ✗ default:
114 ✗ usage(argv[0]);
115 ✗ exit(EXIT_FAILURE);
116 }
117 23 }
118 12 }
119
120 /*
121 * Set the value of one (or more FIXME) channels in the channel data memory map
122 */
123 2 void SetChannelMemoryMap(const std::string& channels, int value) {
124
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (value == -1) {
125
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 std::string payload = "{\"delete\": true}";
126
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 std::string url = "http://localhost:32322/overlays/range/" + channels;
127 1 std::string response;
128
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 urlPut(url, payload, response);
129
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("Response: %s\n", response.c_str());
130 1 return;
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
2 } else if (value < 0) {
132 ✗ std::string payload = "{\"deleteAll\": true}";
133 ✗ std::string url = "http://localhost:32322/overlays/range";
134 ✗ std::string response;
135 ✗ urlPut(url, payload, response);
136 ✗ printf("Response: %s\n", response.c_str());
137 ✗ return;
138 ✗ }
139
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 std::string payload = "{\"Value\": " + std::to_string(value) + "}";
140
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 std::string url = "http://localhost:32322/overlays/range/" + channels;
141 1 std::string response;
142
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 urlPut(url, payload, response);
143
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("Response: %s\n", response.c_str());
144 1 }
145
146 /*
147 * Set a channel data map block as active/inactive.
148 */
149 3 void SetBlockActive(const std::string& blockName, int active) {
150
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 std::string payload = "{\"State\": " + std::to_string(active) + "}";
151
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 std::string url = "http://localhost:32322/overlays/model/" + blockName + "/state";
152 3 std::string response;
153
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 urlPut(url, payload, response);
154
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 printf("Response: %s\n", response.c_str());
155 3 }
156
157 /*
158 * Copy the raw data in a file into a model's overlay buffer.
159 *
160 * Through fppd's API, as the other options here are. This used to map the
161 * model's shared overlay buffer directly, sized for 3 bytes a pixel: an RGBW
162 * model's data overran the mapping, and a model whose buffer fppd hadn't made
163 * yet got an empty one created, and SIGBUS on the first write to it.
164 */
165 5 void CopyFileToMappedBlock(const std::string& blockName, char* inputFilename) {
166
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 std::string url = "http://localhost:32322/overlays/model/" + blockName;
167 5 std::string response;
168
3/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
5 if (!urlGet(url, response)) {
169 ✗ printf("ERROR: Could not find model %s\n", blockName.c_str());
170 ✗ return;
171 }
172
173
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 Json::Value v = LoadJsonFromString(response);
174
7/10
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 4 times.
5 if (!v.isObject() || !v.isMember("Name")) {
175
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("ERROR: Could not find model %s\n", blockName.c_str());
176 1 return;
177 }
178
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 int width = v["width"].asInt();
179
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 int height = v["height"].asInt();
180
4/8
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
4 int bytesPerPixel = (width > 0 && height > 0) ? v["ChannelCount"].asInt() / (width * height) : 0;
181
4/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
4 if (bytesPerPixel != 1 && bytesPerPixel != 3 && bytesPerPixel != 4) {
182 ✗ printf("ERROR: Model %s is not a 1, 3 or 4 channel per pixel model\n", blockName.c_str());
183 ✗ return;
184 }
185
186
3/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 3 times.
4 if (!FileExists(inputFilename)) {
187
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("ERROR: Input file %s does not exist!\n", inputFilename);
188 1 return;
189 }
190
191
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 std::string data = GetFileContents(inputFilename);
192 3 size_t expected = (size_t)width * height * bytesPerPixel;
193
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (data.size() < expected) {
194
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("WARNING: Expected %zu bytes of data but only read %zu.\n", expected, data.size());
195 1 return;
196 }
197
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 data.resize(expected);
198
199
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
2 const char* fmt = bytesPerPixel == 4 ? "rgbw" : (bytesPerPixel == 1 ? "mono" : "rgb");
200
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 url += std::string("/data?target=overlay&stopEffect=false&fmt=") + fmt;
201
4/8
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
2 if (!urlPut(url, data, response)) {
202 ✗ printf("ERROR: Could not send the data to model %s\n", blockName.c_str());
203 ✗ return;
204 }
205
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 printf("Response: %s\n", response.c_str());
206
8/8
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 3 times.
12 }
207
208 /*
209 * Fill a channel data block with a single specified value
210 */
211 1 void FillMappedBlock(const std::string& blockName, int channelData) {
212
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 std::string payload = "{ \"Value\": " + std::to_string(channelData) + " }";
213
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 std::string url = "http://localhost:32322/overlays/model/" + blockName + "/fill";
214 1 std::string response;
215
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 urlPut(url, payload, response);
216
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("Response: %s\n", response.c_str());
217 1 }
218
219 /*
220 * Display info on a named channel data block
221 */
222 1 void DumpMappedBlockInfo(const std::string& blockName) {
223
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 std::string url = "http://localhost:32322/overlays/model/" + blockName;
224 1 std::string response;
225
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 if (!urlGet(url, response)) {
226 ✗ printf("ERROR: Could not find model %s\n", blockName.c_str());
227 ✗ return;
228 }
229
230
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 Json::Value v = LoadJsonFromString(response);
231
232
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 printf("Block Name: %s\n", v["Name"].asString().c_str());
233
234
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 int sc = v["StartChannel"].asInt();
235
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 int cc = v["ChannelCount"].asInt();
236
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("Channels : %d-%d (%d channels)\n", sc, sc + cc - 1, cc);
237
238
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 printf("String Cnt: %d\n", v["StringCount"].asInt());
239
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 printf("Strand Cnt: %d\n", v["StrandsPerString"].asInt());
240
5/10
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 printf("Layout : %dx%d\n", v["width"].asInt(), v["height"].asInt());
241
242
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("Status : ");
243
3/9
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
1 switch (v["isActive"].asInt()) {
244 1 case 0:
245
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("Idle\n");
246 1 break;
247 ✗ case 1:
248 ✗ printf("Active\n");
249 ✗ break;
250 ✗ case 2:
251 ✗ printf("Active (Transparent)\n");
252 ✗ break;
253 ✗ case 3:
254 ✗ printf("Active (Transparent RGB)\n");
255 ✗ break;
256 }
257
258
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("Effect running : ");
259
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 if (v["effectRunning"].asBool()) {
260 ✗ printf("Yes - %s\n", v["effectName"].asString().c_str());
261 } else {
262
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("No\n");
263 }
264
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 }
265
266 /*
267 *
268 */
269 12 int main(int argc, char* argv[]) {
270 12 parseArguments(argc, argv);
271
272
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
12 if (blockName) {
273
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 7 times.
10 if (isActive >= 0)
274
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
9 SetBlockActive(blockName, isActive);
275
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 else if (inputFilename)
276
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
15 CopyFileToMappedBlock(blockName, inputFilename);
277
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 else if (channelData >= 0)
278
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
3 FillMappedBlock(blockName, channelData);
279 else
280
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 DumpMappedBlockInfo(blockName);
281 10 free(blockName);
282
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 } else if (channels) {
283
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (channelData > 255)
284 ✗ channelData = 255;
285
286
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 SetChannelMemoryMap(channels, channelData);
287 2 free(channels);
288 } else {
289 ✗ usage(argv[0]);
290 ✗ exit(EXIT_FAILURE);
291 }
292 12 exit(EXIT_SUCCESS);
293 }
294