2
* Compiz fragment program parser
6
* This should be usable on almost any plugin that wishes to parse fragment
7
* program files separately, maybe it should become a separate plugin?
9
* Author : Guillaume Seguin
10
* Email : guillaume@segu.in
12
* Copyright (c) 2007 Guillaume Seguin <guillaume@segu.in>
14
* Port to std::string:
15
* Copyright (c) 2010 Canonical Ltd. <sam.spilsbury@canonical.com>
17
* This program is free software; you can redistribute it and/or
18
* modify it under the terms of the GNU General Public License
19
* as published by the Free Software Foundation; either version 2
20
* of the License, or (at your option) any later version.
22
* This program is distributed in the hope that it will be useful,
23
* but WITHOUT ANY WARRANTY; without even the implied warranty of
24
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
* GNU General Public License for more details.
27
* You should have received a copy of the GNU General Public License
28
* along with this program; if not, write to the Free Software
29
* Foundation, Inc., 51 Franklin Street,
30
* Fifth Floor, Boston, MA 02110-1301, USA.
45
* Left trimming function
48
FragmentParser::ltrim (const CompString &string)
51
while (!(pos >= string.size ()))
53
if (isspace (string.at (pos)))
59
return string.substr (pos);
62
/* General fragment program related functions ----------------------- */
65
* Clean program name string
68
FragmentParser::programCleanName (CompString &name)
72
/* Replace every non alphanumeric char by '_' */
73
while (!(pos >= name.size ()))
75
if (!isalnum (name.at (pos)))
83
* File reader function
86
FragmentParser::programReadSource (const CompString &fname)
91
CompString data, path, home = CompString (getenv ("HOME"));
93
/* Try to open file fname as is */
94
fp.open (fname.c_str ());
96
/* If failed, try as user filter file (in ~/.compiz/data/filters) */
97
if (!fp.is_open () && !home.empty ())
99
path = home + "/.compiz/data/filters/" + fname;
100
fp.open (path.c_str ());
103
/* If failed again, try as system wide data file
104
* (in PREFIX/share/compiz/filters) */
107
path = CompString (DATADIR) + "/data/filters/" + fname;
108
fp.open (path.c_str ());
111
/* If failed again & again, abort */
114
return CompString ("");
117
/* get length of file: */
118
fp.seekg (0, std::ios::end);
119
length = fp.tellg ();
121
fp.seekg (0, std::ios::beg);
123
/* allocate memory */
124
buffer = new char [length];
126
/* read data as a block: */
127
fp.read (buffer, length - 1);
128
buffer[length - 1] = '\0';
139
* Get the first "argument" in the given string, trimmed
140
* and move source string pointer after the end of the argument.
141
* For instance in string " foo, bar" this function will return "foo".
143
* This function returns NULL if no argument found
144
* or a malloc'ed string that will have to be freed later.
147
FragmentParser::getFirstArgument (const CompString &line,
152
size_t next, temp, orig;
156
if (pos >= line.size ())
157
return CompString ("");
160
string = FragmentParser::ltrim (line.substr (pos));
165
/* Find next comma or semicolon (which isn't that useful since we
166
* are working on tokens delimited by semicolons) */
167
if ((next = string.find (",", pos)) != std::string::npos ||
168
(next = string.find (";", pos)) != std::string::npos)
174
return getFirstArgument (line, pos);
176
if ((temp = string.find ("{", pos) != std::string::npos) && temp < next &&
177
(temp = string.find ("}", pos) != std::string::npos) && temp > next)
179
if ((next = string.find (",", temp)) != std::string::npos ||
180
(next = string.find (";", temp)) != std::string::npos)
183
length = string.substr (pos).size ();
187
length = string.substr (pos).size ();
189
/* Allocate, copy and end string */
190
arg = string.substr (pos, length);
192
/* Increment source pointer */
193
if ((orig + arg.size () + 1) <= line.size ())
194
pos += orig + arg.size () + 1;
196
pos = std::string::npos;
201
/* Texture offset related functions ----------------------------------*/
204
* Add a new fragment offset to the offsets stack from an ADD op string
206
FragmentParser::FragmentOffset *
207
FragmentParser::programAddOffsetFromAddOp (const CompString &source)
209
FragmentOffset offset;
213
CompString offsetString;
215
std::list <FragmentOffset>::iterator it = offsets.begin ();
217
if (source.size () < 5)
222
name = getFirstArgument (op, pos);
226
temp = getFirstArgument (op, pos);
228
/* If an offset with the same name is
229
* already registered, skip this one */
230
if ((!offsets.empty () &&
231
!programFindOffset (it, name).empty ()) ||
235
/* Just use the end of the op as the offset */
237
offsetString = ltrim (op.substr (pos));
238
if (offsetString.empty ())
242
offset.offset = offsetString;
244
offsets.push_back (offset);
246
return &(offsets.back ());
250
* Find an offset according to its name
253
FragmentParser::programFindOffset (std::list<FragmentOffset>::iterator it,
254
const CompString &name)
256
if (it->name == name)
259
return programFindOffset ((it++), name);
263
* Recursively free offsets stack
266
FragmentParser::programFreeOffset ()
271
/* Actual parsing/loading functions ----------------------------------*/
274
* Parse the source buffer op by op and add each op to function data
276
* FIXME : I am more than 200 lines long, I feel so heavy!
279
FragmentParser::programParseSource (GLFragment::FunctionData *data,
280
int target, CompString &source)
282
CompString line, next;
285
size_t pos = 0, strippos = 0;
286
int length, oplength, type;
288
CompString arg1, arg2, temp;
290
/* Find the header, skip it, and start parsing from there */
292
pos = source.find ("!!ARBfp1.0", pos);
293
if (pos != std::string::npos)
299
while ((strippos = source.find ("#", strippos)) != std::string::npos)
301
size_t carriagepos = source.find ("\n", strippos);
303
if (carriagepos != std::string::npos)
305
source.erase (strippos, carriagepos - strippos);
309
source = source.substr (0, strippos);
314
/* Strip linefeeds */
315
while ((strippos = source.find ("\n", strippos)) != std::string::npos)
316
source[strippos] = ' ';
318
/* Parse each instruction */
319
while (!(pos >= (source.size () - 1)))
321
size_t nPos = source.find (";", pos + 1);
322
line = source.substr (pos + 1, nPos - pos);
323
CompString origcurrent = current = ltrim (line);
324
/* Find instruction type */
328
if (current.substr (0, 3) == "END")
330
else if (current.substr (0, 3) == "ABS" ||
331
current.substr (0, 3) == "CMP" ||
332
current.substr (0, 3) == "COS" ||
333
current.substr (0, 3) == "DP3" ||
334
current.substr (0, 3) == "DP4" ||
335
current.substr (0, 3) == "EX2" ||
336
current.substr (0, 3) == "FLR" ||
337
current.substr (0, 3) == "FRC" ||
338
current.substr (0, 3) == "KIL" ||
339
current.substr (0, 3) == "LG2" ||
340
current.substr (0, 3) == "LIT" ||
341
current.substr (0, 3) == "LRP" ||
342
current.substr (0, 3) == "MAD" ||
343
current.substr (0, 3) == "MAX" ||
344
current.substr (0, 3) == "MIN" ||
345
current.substr (0, 3) == "POW" ||
346
current.substr (0, 3) == "RCP" ||
347
current.substr (0, 3) == "RSQ" ||
348
current.substr (0, 3) == "SCS" ||
349
current.substr (0, 3) == "SIN" ||
350
current.substr (0, 3) == "SGE" ||
351
current.substr (0, 3) == "SLT" ||
352
current.substr (0, 3) == "SUB" ||
353
current.substr (0, 3) == "SWZ" ||
354
current.substr (0, 3) == "TXP" ||
355
current.substr (0, 3) == "TXB" ||
356
current.substr (0, 3) == "XPD")
358
else if (current.substr (0, 4) == "TEMP")
360
else if (current.substr (0, 5) == "PARAM")
362
else if (current.substr (0, 6) == "ATTRIB")
364
else if (current.substr (0, 3) == "TEX")
366
else if (current.substr (0, 3) == "ADD")
368
if (current.find ("fragment.texcoord", 0) != std::string::npos)
369
programAddOffsetFromAddOp (current.c_str ());
373
else if (current.substr (0, 3) == "MUL")
375
if (current.find ("fragment.color", 0) != std::string::npos)
380
else if (current.substr (0, 3) == "MOV")
382
if (current.find ("result.color", 0) != std::string::npos)
390
/* Data op : just copy paste the
391
* whole instruction plus a ";" */
393
data->addDataOp (current.c_str ());
395
/* Parse arguments one by one */
400
if (type == TempOp) oplength = 4;
401
else if (type == ParamOp) oplength = 5;
402
else if (type == AttribOp) oplength = 6;
403
length = current.size ();
404
if (length < oplength + 2) break;
408
while (current.size () && !(cpos >= current.size ()) &&
409
(arg1 = getFirstArgument (current, cpos)).size ())
411
/* "output" is a reserved word, skip it */
412
if (arg1.substr (0, 6) == "output")
416
data->addTempHeaderOp (arg1.c_str ());
417
else if (type == ParamOp)
418
data->addParamHeaderOp (arg1.c_str ());
419
else if (type == AttribOp)
420
data->addAttribHeaderOp (arg1.c_str ());
426
/* Example : TEX tmp, coord, texture[0], RECT;
427
* "tmp" is dest name, while "coord" is either
428
* fragment.texcoord[0] or an offset */
431
if ((arg1 = getFirstArgument (current, cpos)).size ())
433
if (!(temp = getFirstArgument (current, cpos)).size ())
436
if (temp == "fragment.texcoord[0]")
437
data->addFetchOp (arg1.c_str (), NULL, target);
438
else if (offsets.size ())
440
arg2 = programFindOffset (
444
data->addFetchOp (arg1.c_str (),
445
arg2.c_str (), target);
452
if (current.substr (0, 3) == "MUL") /* MUL op, 2 ops */
454
/* Example : MUL output, fragment.color, output;
455
* MOV arg1, fragment.color, arg2 */
458
if (!(arg1 = getFirstArgument (current, cpos)).size ())
463
if (!(temp = getFirstArgument (current, cpos)).size ())
466
if (!(arg2 = getFirstArgument (current, cpos)).size ())
469
data->addColorOp (arg1.c_str (), arg2.c_str ());
471
else /* MOV op, 1 op */
473
/* Example : MOV result.color, output;
474
* MOV result.color, arg1; */
475
cpos = current.find (",") + 1;
477
if ((arg1 = getFirstArgument (current, cpos)).size ())
478
data->addColorOp ("output", arg1.c_str ());
487
programFreeOffset ();
491
* Build a Compiz Fragment Function from a source string
493
GLFragment::FunctionId
494
FragmentParser::buildFragmentProgram (CompString &source,
495
const CompString &name,
498
GLFragment::FunctionData *data;
500
/* Create the function data */
501
data = new GLFragment::FunctionData ();
504
/* Parse the source and fill the function data */
505
programParseSource (data, target, source);
506
/* Create the function */
507
handle = data->createFragmentFunction (name.c_str ());
513
* Load a source file and build a Compiz Fragment Function from it
515
GLFragment::FunctionId
516
FragmentParser::loadFragmentProgram (const CompString &file,
521
GLFragment::FunctionId handle;
523
/* Clean fragment program name */
524
programCleanName (name);
525
/* Read the source file */
526
source = programReadSource (file);
532
/* Build the Compiz Fragment Program */
533
handle = buildFragmentProgram (source,