Basic drawing script parsing and execution is working.

This commit is contained in:
Scott Duensing 2022-11-26 17:53:53 -06:00
parent e49b5793fc
commit 7551144902
5 changed files with 247 additions and 65 deletions

View file

@ -26,11 +26,13 @@ project(joeydev C)
set(CMAKE_C_STANDARD 99)
set(SOURCE_FILES
src/main.c
src/utils.c
src/joeydev.c
src/vector.c
src/array.c src/draw.c)
src/main.c
src/utils.c
src/joeydev.c
src/vector.c
src/array.c
src/draw.c
)
add_executable(${CMAKE_PROJECT_NAME} ${SOURCE_FILES})

View file

@ -0,0 +1,35 @@
/*
* JoeyDev
* Copyright (C) 2018-2023 Scott Duensing <scott@kangaroopunch.com>
*
* 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.
*/
#ifndef SCINTILLAHEADERS_H
#define SCINTILLAHEADERS_H
#define PLAT_GTK 1
#define GTK 3
#include "Scintilla.h"
#include "SciLexer.h"
#include "ScintillaWidget.h"
#include "Lexilla.h"
#endif //SCINTILLAHEADERS_H

View file

@ -352,11 +352,11 @@ void jlDrawLine(jlContextT *c, jint16 x1, jint16 y1, jint16 x2, jint16 y2) {
jbyte jlDrawPixelGet(jlContextT *c, jint16 x, jint16 y) {
int offset = (x + y * 320) * 4;
int r = c->_pixels[offset + 1];
int g = c->_pixels[offset + 2];
int b = c->_pixels[offset + 3];
int index = 0;
unsigned int offset = (x + y * 320) * 4;
int r = c->_pixels[offset + 2];
int g = c->_pixels[offset + 1];
int b = c->_pixels[offset];
int index = 0;
int i;
// Find the palette index for this color.
@ -372,12 +372,13 @@ jbyte jlDrawPixelGet(jlContextT *c, jint16 x, jint16 y) {
void jlDrawPixelSet(jlContextT *c, jint16 x, jint16 y) {
int offset = (x + y * 320) * 4;
unsigned int offset = (x + y * 320) * 4;
c->_pixels[offset] = 255;
c->_pixels[offset + 1] = c->_jlPalette[c->_jlDrawColor].r;
c->_pixels[offset + 2] = c->_jlPalette[c->_jlDrawColor].g;
c->_pixels[offset + 3] = c->_jlPalette[c->_jlDrawColor].b;
c->_pixels[offset] = c->_jlPalette[c->_jlDrawColor].b;
c->_pixels[offset + 1] = c->_jlPalette[c->_jlDrawColor].g;
c->_pixels[offset + 2] = c->_jlPalette[c->_jlDrawColor].r;
// We're using CAIRO_FORMAT_RGB24 so the upper 8 bits are not used.
//c->_pixels[offset + 3] = 255; // This is alpha in CAIRO_FORMAT_ARGB32 mode.
}

View file

@ -21,6 +21,7 @@
#include "common.h"
#include "scintillaHeaders.h"
#include "joeydev.h"
#include "stddclmr.h"
@ -30,6 +31,7 @@ int main(int argc, char **argv) {
gtk_init(&argc, &argv);
winJoeyDevCreate();
gtk_main();
scintilla_release_resources();
return 0;
}

View file

@ -28,14 +28,7 @@
#include "common.h"
#include <string.h>
#include <errno.h>
#define PLAT_GTK 1
#define GTK 3
#include "Scintilla.h"
#include "SciLexer.h"
#include "ScintillaWidget.h"
#include "Lexilla.h"
#include "scintillaHeaders.h"
#include "gladeVector.h"
#include "vector.h"
#include "utils.h"
@ -44,6 +37,8 @@
#define SSM(m, w, l) scintilla_send_message(self->sci, m, w, l)
#define MARGIN_SCRIPT_FOLD_INDEX 1
#define MARKER_ERROR_ARROW 0
#define MARKER_ERROR_HIGHLIGHT 1
enum PassE {
@ -74,6 +69,12 @@ typedef struct CommandsS {
} CommandsT;
typedef struct PointS {
int x;
int y;
} PointT;
static int _nextEditorId = 0;
@ -82,27 +83,30 @@ EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCN
EVENT void menuVectorFileClose(GtkWidget *object, gpointer userData);
static gboolean parseBox(PassT pass, char **tokenEnd, VectorDataT *self);
static gboolean parseCircle(PassT pass, char **tokenEnd, VectorDataT *self);
static gboolean parseClear(PassT pass, char **tokenEnd, VectorDataT *self);
static gboolean parseColor(PassT pass, char **tokenEnd, VectorDataT *self);
static gboolean parseComment(PassT pass, char **tokenEnd, VectorDataT *self);
static gboolean parseEllipse(PassT pass, char **tokenEnd, VectorDataT *self);
static gboolean parseFill(PassT pass, char **tokenEnd, VectorDataT *self);
static gboolean parseLine(PassT pass, char **tokenEnd, VectorDataT *self);
static gboolean parsePalette(PassT pass, char **tokenEnd, VectorDataT *self);
static gboolean parsePlot(PassT pass, char **tokenEnd, VectorDataT *self);
static gboolean parseRectangle(PassT pass, char **tokenEnd, VectorDataT *self);
static gboolean parseReset(PassT pass, char **tokenEnd, VectorDataT *self);
static void parser(PassT pass, gpointer userData);
static gboolean parserGetNextValue(char *token, char **valueEnd, int *x);
static gboolean parserGetWord(char *word, char **tokenEnd);
static gboolean parserGetX(char **tokenEnd, int *x);
static gboolean parserGetXY(char **tokenEnd, int *x, int *y);
static gboolean parserGetXYZ(char **tokenEnd, int *x, int *y, int *z);
EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData);
static void winVectorDelete(gpointer userData);
EVENT gboolean drawVectorImageDraw(GtkWidget *widget, cairo_t *cr, gpointer userData) {
VectorDataT *self = (VectorDataT *)userData;
cairo_surface_t *target = cairo_get_target(cr);
int width = cairo_image_surface_get_width(target);
int height = cairo_image_surface_get_height(target);
int width = cairo_image_surface_get_width(self->surface);
int height = cairo_image_surface_get_height(self->surface);
(void)widget;
(void)userData;
@ -118,39 +122,36 @@ EVENT gboolean drawVectorImageDraw(GtkWidget *widget, cairo_t *cr, gpointer user
cairo_surface_flush(self->surface);
printf("Redrew image\n");
return FALSE;
}
EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotification *notifyData, gpointer userData) {
VectorDataT *self = (VectorDataT *)userData;
int lineNumber = (int)SSM(SCI_LINEFROMPOSITION, (uptr_t)notifyData->position , (sptr_t)0);
int lineNumber = (int)SSM(SCI_LINEFROMPOSITION, (uptr_t)notifyData->position, (sptr_t)0);
(void)sciWidget;
(void)ctrlID;
//printf("Notification %d\n", notifyData->nmhdr.code);
//printf("Notification %d\n", notifyData->modificationType);
switch (notifyData->nmhdr.code) {
case SCN_CHARADDED:
parser(PASS_DRAW, userData);
gtk_widget_queue_draw(self->drawVectorImage);
break;
case SCN_SAVEPOINTLEFT:
self->windowData.isDirty = TRUE; //***TODO*** I don't think this is very accurate.
break;
case SCN_SAVEPOINTREACHED:
self->windowData.isDirty = FALSE;
case SCN_MODIFIED:
if (notifyData->modificationType & SC_MOD_INSERTTEXT || notifyData->modificationType & SC_MOD_DELETETEXT) {
// Parse code.
parser(PASS_DRAW, userData);
// Refresh widget.
gtk_widget_queue_draw(self->drawVectorImage);
// Mark text dirty. SCN_SAVEPOINTLEFT isn't being reliable.
self->windowData.isDirty = TRUE;
}
break;
case SCN_MARGINCLICK:
switch (notifyData->margin) {
case MARGIN_SCRIPT_FOLD_INDEX:
SSM(SCI_TOGGLEFOLD, lineNumber, (sptr_t)0);
SSM(SCI_TOGGLEFOLD, lineNumber, (sptr_t) 0);
break;
}
break;
@ -177,6 +178,8 @@ static gboolean parseBox(PassT pass, char **tokenEnd, VectorDataT *self) {
if (!parserGetXY(tokenEnd, &x1, &y1)) return FALSE;
if (!parserGetWord("TO", tokenEnd)) return FALSE;
if (!parserGetXY(tokenEnd, &x2, &y2)) return FALSE;
if (x2 < x1 || x1 < 0 || x2 < 0 || x1 > 319 || x2 > 319) return FALSE;
if (y2 < y1 || y1 < 0 || y2 < 0 || y1 > 199 || y2 > 199) return FALSE;
switch (pass) {
case PASS_DRAW:
@ -200,6 +203,8 @@ static gboolean parseCircle(PassT pass, char **tokenEnd, VectorDataT *self) {
if (!parserGetX(tokenEnd, &r)) return FALSE;
if (!parserGetWord("AT", tokenEnd)) return FALSE;
if (!parserGetXY(tokenEnd, &x1, &y1)) return FALSE;
if (x1 < 0 || x1 > 319) return FALSE;
if (y1 < 0 || y1 > 199) return FALSE;
switch (pass) {
case PASS_DRAW:
@ -214,12 +219,27 @@ static gboolean parseCircle(PassT pass, char **tokenEnd, VectorDataT *self) {
}
static gboolean parseClear(PassT pass, char **tokenEnd, VectorDataT *self) {
switch (pass) {
case PASS_DRAW:
jlDrawClear(self->jlc);
break;
case PASS_GENERATE:
break;
}
return TRUE;
}
static gboolean parseColor(PassT pass, char **tokenEnd, VectorDataT *self) {
int c;
// Color (short)
if (!parserGetX(tokenEnd, &c)) return FALSE;
if (c < 0 || c > 15) return FALSE;
switch (pass) {
case PASS_DRAW:
@ -256,6 +276,8 @@ static gboolean parseEllipse(PassT pass, char **tokenEnd, VectorDataT *self) {
if (!parserGetXY(tokenEnd, &x1, &y1)) return FALSE;
if (!parserGetWord("TO", tokenEnd)) return FALSE;
if (!parserGetXY(tokenEnd, &x2, &y2)) return FALSE;
if (x2 < x1 || x1 < 0 || x2 < 0 || x1 > 319 || x2 > 319) return FALSE;
if (y2 < y1 || y1 < 0 || y2 < 0 || y1 > 199 || y2 > 199) return FALSE;
switch (pass) {
case PASS_DRAW:
@ -271,10 +293,27 @@ static gboolean parseEllipse(PassT pass, char **tokenEnd, VectorDataT *self) {
static gboolean parseFill(PassT pass, char **tokenEnd, VectorDataT *self) {
// Fill (on|to) (value),(value)
int c = 16;
int x1;
int y1;
// Fill (value),(value) {to (value}
if (!parserGetXY(tokenEnd, &x1, &y1)) return FALSE;
if (x1 < 0 || x1 > 319) return FALSE;
if (y1 < 0 || y1 > 199) return FALSE;
// Do they want to fill to a certain color? Or over the current color?
if (parserGetWord("TO", tokenEnd)) {
if (!parserGetX(tokenEnd, &c)) return FALSE;
if (c < 0 || c > 15) return FALSE;
}
switch (pass) {
case PASS_DRAW:
if (c > 15) {
jlDrawFill(self->jlc, x1, y1);
} else {
jlDrawFillTo(self->jlc, x1, y1, c);
}
break;
case PASS_GENERATE:
@ -286,13 +325,67 @@ static gboolean parseFill(PassT pass, char **tokenEnd, VectorDataT *self) {
static gboolean parseLine(PassT pass, char **tokenEnd, VectorDataT *self) {
int x;
PointT p1;
PointT p2;
PointT *points = NULL;
// Line (value),(value) to (value),(value) [to ...]
if (!parserGetXY(tokenEnd, &x1, &y1)) return FALSE;
if (!parserGetXY(tokenEnd, &p1.x, &p1.y)) return FALSE;
if (!parserGetWord("TO", tokenEnd)) return FALSE;
if (!parserGetXY(tokenEnd, &x2, &y2)) return FALSE;
if (!parserGetXY(tokenEnd, &p2.x, &p2.y)) return FALSE;
if (p1.x < 0 || p1.x > 319) return FALSE;
if (p1.y < 0 || p1.y > 199) return FALSE;
if (p2.x < 0 || p2.x > 319) return FALSE;
if (p2.y < 0 || p2.y > 199) return FALSE;
arrput(points, p1);
arrput(points, p2);
while (parserGetWord("TO", tokenEnd)) {
if (!parserGetXY(tokenEnd, &p1.x, &p1.y)) {
// Error. Unwind array.
while (arrlen(points) > 0) arrdel(points, 0);
arrfree(points);
return FALSE;
}
arrput(points, p1);
}
switch (pass) {
case PASS_DRAW:
for (x=0; x<arrlen(points)-1; x++) {
jlDrawLine(self->jlc, points[x].x, points[x].y, points[x+1].x, points[x+1].y);
}
break;
case PASS_GENERATE:
break;
}
// Unwind array.
while (arrlen(points) > 0) arrdel(points, 0);
arrfree(points);
return TRUE;
}
static gboolean parsePalette(PassT pass, char **tokenEnd, VectorDataT *self) {
int i;
int r;
int g;
int b;
if (!parserGetX(tokenEnd, &i)) return FALSE;
if (!parserGetWord("AS", tokenEnd)) return FALSE;
if (!parserGetXYZ(tokenEnd, &r, &g, &b)) return FALSE;
if (i < 0 || i > 15) return FALSE;
if (r < 0 || r > 15) return FALSE;
if (g < 0 || g > 15) return FALSE;
if (b < 0 || b > 15) return FALSE;
switch (pass) {
case PASS_DRAW:
jlPaletteSet(self->jlc, i, r, g, b);
break;
case PASS_GENERATE:
@ -309,6 +402,8 @@ static gboolean parsePlot(PassT pass, char **tokenEnd, VectorDataT *self) {
// Plot (value),(value)
if (!parserGetXY(tokenEnd, &x1, &y1)) return FALSE;
if (x1 < 0 || x1 > 319) return FALSE;
if (y1 < 0 || y1 > 199) return FALSE;
switch (pass) {
case PASS_DRAW:
@ -333,6 +428,8 @@ static gboolean parseRectangle(PassT pass, char **tokenEnd, VectorDataT *self) {
if (!parserGetXY(tokenEnd, &x1, &y1)) return FALSE;
if (!parserGetWord("TO", tokenEnd)) return FALSE;
if (!parserGetXY(tokenEnd, &x2, &y2)) return FALSE;
if (x2 < x1 || x1 < 0 || x2 < 0 || x1 > 319 || x2 > 319) return FALSE;
if (y2 < y1 || y1 < 0 || y2 < 0 || y1 > 199 || y2 > 199) return FALSE;
switch (pass) {
case PASS_DRAW:
@ -373,6 +470,7 @@ static void parser(PassT pass, gpointer userData) {
VectorDataT *self = (VectorDataT *)userData;
int length = SSM(SCI_GETLENGTH, 0, 0);
int x;
int lineNumber = 0;
char *code;
char *line;
char *lineEnd;
@ -382,11 +480,13 @@ static void parser(PassT pass, gpointer userData) {
CommandsT commands[] = {
{ "BOX", parseBox },
{ "CIRCLE", parseCircle },
{ "CLEAR", parseClear },
{ "COLOR", parseColor },
{ "//", parseComment },
{ "ELLIPSE", parseEllipse },
{ "FILL", parseFill },
{ "LINE", parseLine },
{ "PALETTE", parsePalette },
{ "PLOT", parsePlot },
{ "RECTANGLE", parseRectangle },
{ "RESET", parseReset },
@ -400,6 +500,12 @@ static void parser(PassT pass, gpointer userData) {
// Fetch code.
SSM(SCI_GETTEXT, length, (sptr_t)code);
if (pass == PASS_DRAW) {
// Clear error markers.
SSM(SCI_MARKERDELETEALL, MARKER_ERROR_ARROW, 0);
SSM(SCI_MARKERDELETEALL, MARKER_ERROR_HIGHLIGHT, 0);
}
// Parse code.
line = strtok_r(code, "\n", &lineEnd);
while (line != NULL) {
@ -417,11 +523,14 @@ static void parser(PassT pass, gpointer userData) {
}
x++;
}
if (lineOkay == FALSE) {
//***TODO*** Mark lines that fail to parse.
if (lineOkay == FALSE && pass == PASS_DRAW) {
// Mark lines that fail to parse.
SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_ARROW);
SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_HIGHLIGHT);
}
// Move to next line.
line = strtok_r(NULL, "\n", &lineEnd);
lineNumber++;
}
// Release code.
@ -450,6 +559,24 @@ static void parser(PassT pass, gpointer userData) {
*/
}
static gboolean parserGetNextValue(char *token, char **valueEnd, int *x) {
char *value;
char *endPtr = NULL;
// Return next value in a comma separated list.
//***TODO*** Variable support.
value = strtok_r(token, ",", valueEnd);
if (value == NULL) return FALSE;
errno = 0; endPtr = NULL;
*x = (int)strtol(value, &endPtr, 10);
if (errno != 0) return FALSE;
return TRUE;
}
static gboolean parserGetWord(char *word, char **tokenEnd) {
char *token;
@ -482,27 +609,32 @@ static gboolean parserGetX(char **tokenEnd, int *x) {
static gboolean parserGetXY(char **tokenEnd, int *x, int *y) {
char *token;
char *value;
char *valueEnd;
char *endPtr;
// Return values of X,Y pair.
//***TODO*** Variable support.
token = strtok_r(NULL, " ", tokenEnd);
if (token == NULL) return FALSE;
value = strtok_r(token, ",", &valueEnd);
if (value == NULL) return FALSE;
errno = 0; endPtr = NULL;
*x = (int)strtol(value, &endPtr, 10);
if (errno != 0) return FALSE;
if (!parserGetNextValue(token, &valueEnd, x)) return FALSE;
if (!parserGetNextValue(NULL, &valueEnd, y)) return FALSE;
value = strtok_r(NULL, ",", &valueEnd);
if (value == NULL) return FALSE;
errno = 0; endPtr = NULL;
*y = (int)strtol(value, &endPtr, 10);
if (errno != 0) return FALSE;
return TRUE;
}
static gboolean parserGetXYZ(char **tokenEnd, int *x, int *y, int *z) {
char *token;
char *valueEnd;
// Return values of X,Y pair.
token = strtok_r(NULL, " ", tokenEnd);
if (token == NULL) return FALSE;
if (!parserGetNextValue(token, &valueEnd, x)) return FALSE;
if (!parserGetNextValue(NULL, &valueEnd, y)) return FALSE;
if (!parserGetNextValue(NULL, &valueEnd, z)) return FALSE;
return TRUE;
}
@ -560,8 +692,8 @@ void winVectorCreate(void) {
SSM(SCI_STYLESETBACK, STYLE_DEFAULT, 0);
SSM(SCI_STYLECLEARALL, 0, 0);
SSM(SCI_SETTABWIDTH, 3, 0);
SSM(SCI_SETMARGINWIDTHN, 0, (int)SSM(SCI_TEXTWIDTH, STYLE_LINENUMBER, (sptr_t)"99999"));
SSM(SCI_SETMARGINWIDTHN, 1, 0);
SSM(SCI_SETMARGINWIDTHN, 0, (int)SSM(SCI_TEXTWIDTH, STYLE_LINENUMBER, (sptr_t)"_99999"));
SSM(SCI_SETMARGINWIDTHN, 1, 16);
SSM(SCI_SETWRAPMODE, SC_WRAP_WORD, 0);
SSM(SCI_SETWRAPVISUALFLAGS, SC_WRAPVISUALFLAG_END, 0);
SSM(SCI_SETWRAPINDENTMODE, SC_WRAPINDENT_INDENT, 0);
@ -584,18 +716,25 @@ void winVectorCreate(void) {
SSM(SCI_STYLESETFORE, SCE_C_STRING, 0xFF00FF);
SSM(SCI_STYLESETBOLD, SCE_C_OPERATOR, 1);
// Margin markers.
SSM(SCI_MARKERDEFINE, MARKER_ERROR_ARROW, SC_MARK_SHORTARROW); // Error
SSM(SCI_MARKERSETBACK, MARKER_ERROR_ARROW, 255 | (0 << 8) | (0 << 16)); // RGB
SSM(SCI_MARKERDEFINE, MARKER_ERROR_HIGHLIGHT, SC_MARK_BACKGROUND); // Error
SSM(SCI_MARKERSETBACK, MARKER_ERROR_HIGHLIGHT, 127 | (0 << 8) | (0 << 16)); // RGB
// Debug
SSM(SCI_INSERTTEXT, 0, (sptr_t)
"reset\n"
"palette 15 as 15,0,0\n"
"color 15\n"
"box 0,0 to 319,299\n"
"box 0,0 to 319,199\n"
);
// Connect editor to our code.
g_signal_connect(G_OBJECT(self->editor), "sci-notify", G_CALLBACK(editorVectorNotify), self);
// Create our drawing surface and context.
self->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 320, 200);
self->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 320, 200);
self->cr = cairo_create(self->surface);
cairo_surface_flush(self->surface);
self->surfacePointer = cairo_image_surface_get_data(self->surface);
@ -610,6 +749,9 @@ void winVectorCreate(void) {
static void winVectorDelete(gpointer userData) {
VectorDataT *self = (VectorDataT *)userData;
// Scintilla keeps sending events after we delete things it expects to still exist. Prevent that.
g_signal_handlers_disconnect_by_func(G_OBJECT(self->editor), G_CALLBACK(editorVectorNotify), self);
utilWindowUnRegister(userData);
jlContextDel(&self->jlc);