From 7551144902810f4e78466626fd7cc056f9707e44 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sat, 26 Nov 2022 17:53:53 -0600 Subject: [PATCH] Basic drawing script parsing and execution is working. --- CMakeLists.txt | 12 +- include/scintillaHeaders.h | 35 ++++++ src/draw.c | 21 ++-- src/main.c | 2 + src/vector.c | 242 +++++++++++++++++++++++++++++-------- 5 files changed, 247 insertions(+), 65 deletions(-) create mode 100644 include/scintillaHeaders.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e2d9ece..1a28128 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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}) diff --git a/include/scintillaHeaders.h b/include/scintillaHeaders.h new file mode 100644 index 0000000..f04f17b --- /dev/null +++ b/include/scintillaHeaders.h @@ -0,0 +1,35 @@ +/* + * JoeyDev + * Copyright (C) 2018-2023 Scott Duensing + * + * 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 diff --git a/src/draw.c b/src/draw.c index 107efe8..87ac13f 100644 --- a/src/draw.c +++ b/src/draw.c @@ -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. } diff --git a/src/main.c b/src/main.c index f21ee94..d6a7eb7 100644 --- a/src/main.c +++ b/src/main.c @@ -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; } diff --git a/src/vector.c b/src/vector.c index d51901e..61882cb 100644 --- a/src/vector.c +++ b/src/vector.c @@ -28,14 +28,7 @@ #include "common.h" #include #include - -#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; xjlc, 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);