summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--.gitmodules3
-rw-r--r--CMakeLists.txt86
-rw-r--r--LICENSE14
-rw-r--r--README29
-rw-r--r--autistdraw.c232
-rw-r--r--config.h.in11
7 files changed, 379 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1108dc5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+# Backup files
+*.*~
+# IDE project files
+/CMakeLists.txt.user
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..ad38bca
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "termo"]
+ path = termo
+ url = git://github.com/pjanouch/termo.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..08ca44f
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,86 @@
+project (autistdraw C)
+cmake_minimum_required (VERSION 2.8.5)
+
+# Moar warnings
+if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC)
+ set (CMAKE_C_FLAGS "-std=gnu99")
+ set (CMAKE_C_FLAGS_DEBUG
+ "${CMAKE_C_FLAGS_DEBUG} -Wall -Wextra -Wno-missing-field-initializers")
+endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC)
+
+# Build options
+option (USE_SYSTEM_TERMO "Don't compile our own termo, use the system one" OFF)
+
+# Version
+set (project_VERSION_MAJOR "0")
+set (project_VERSION_MINOR "1")
+set (project_VERSION_PATCH "0")
+
+set (project_VERSION "${project_VERSION_MAJOR}")
+set (project_VERSION "${project_VERSION}.${project_VERSION_MINOR}")
+set (project_VERSION "${project_VERSION}.${project_VERSION_PATCH}")
+
+# Dependencies
+find_package (PkgConfig REQUIRED)
+pkg_check_modules (dependencies REQUIRED ncursesw libuv)
+
+if (USE_SYSTEM_TERMO)
+ find_package (Termo REQUIRED)
+else (USE_SYSTEM_TERMO)
+ add_subdirectory (termo EXCLUDE_FROM_ALL)
+ # We don't have many good choices when we don't want to install it and want
+ # to support older versions of CMake; this is a relatively clean approach
+ # (other possibilities: setting a variable in the parent scope, using a
+ # cache variable, writing a special config file with build paths in it and
+ # including it here, or setting a custom property on the targets).
+ get_directory_property (Termo_INCLUDE_DIRS
+ DIRECTORY termo INCLUDE_DIRECTORIES)
+ set (Termo_LIBRARIES termo-static)
+endif (USE_SYSTEM_TERMO)
+
+include_directories (${dependencies_INCLUDE_DIRS} ${Termo_INCLUDE_DIRS})
+
+# Configuration
+include (CheckFunctionExists)
+set (CMAKE_REQUIRED_LIBRARIES ${dependencies_LIBRARIES})
+CHECK_FUNCTION_EXISTS ("resize_term" HAVE_RESIZE_TERM)
+
+# Project source files
+set (project_sources ${PROJECT_NAME}.c)
+set (project_headers ${CMAKE_CURRENT_BINARY_DIR}/config.h)
+
+# Project libraries
+set (project_libraries ${dependencies_LIBRARIES} termo-static)
+
+# Generate a configuration file
+configure_file (${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/config.h)
+include_directories (${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
+
+# Build the main executable and link it
+add_executable (${PROJECT_NAME} ${project_sources} ${project_headers})
+target_link_libraries (${PROJECT_NAME} ${project_libraries})
+
+# The files to be installed
+include (GNUInstallDirs)
+install (TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
+install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
+
+# CPack
+set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Terminal drawing application")
+set (CPACK_PACKAGE_VENDOR "Premysl Janouch")
+set (CPACK_PACKAGE_CONTACT "Přemysl Janouch <p.janouch@gmail.com>")
+set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
+set (CPACK_PACKAGE_VERSION_MAJOR ${project_VERSION_MAJOR})
+set (CPACK_PACKAGE_VERSION_MINOR ${project_VERSION_MINOR})
+set (CPACK_PACKAGE_VERSION_PATCH ${project_VERSION_PATCH})
+set (CPACK_GENERATOR "TGZ;ZIP")
+set (CPACK_PACKAGE_FILE_NAME
+ "${PROJECT_NAME}-${project_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
+set (CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}-${project_VERSION}")
+set (CPACK_SOURCE_GENERATOR "TGZ;ZIP")
+set (CPACK_SOURCE_IGNORE_FILES "/\\\\.git;/build;/CMakeLists.txt.user")
+set (CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${project_VERSION}")
+
+include (CPack)
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..71e1e3e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,14 @@
+ Copyright (c) 2014, Přemysl Janouch <p.janouch@gmail.com>
+ All rights reserved.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/README b/README
new file mode 100644
index 0000000..e981b0a
--- /dev/null
+++ b/README
@@ -0,0 +1,29 @@
+autistdraw
+==========
+
+`autistdraw' will be a terminal drawing application with multiplayer support.
+
+Building and Running
+--------------------
+Build dependencies: GCC/Clang, pkg-config, GNU make, Jansson, cURL, readline
+
+If you don't have Clang, you can edit the Makefile to use GCC or TCC, they work
+just as good. But there's no CMake support yet, so I force it in the Makefile.
+
+ $ git clone https://github.com/pjanouch/autistdraw.git
+ $ git submodule init
+ $ git submodule update
+ $ mkdir build
+ $ cmake .. -DCMAKE_BUILD_TYPE=Debug
+ $ make
+ $ ./autistdraw
+
+License
+-------
+`autistdraw' is written by Přemysl Janouch <p.janouch@gmail.com>.
+
+You may use the software under the terms of the ISC license, the text of which
+is included within the package, or, at your option, you may relicense the work
+under the MIT or the Modified BSD License, as listed at the following site:
+
+http://www.gnu.org/licenses/license-list.html
diff --git a/autistdraw.c b/autistdraw.c
new file mode 100644
index 0000000..ace5e14
--- /dev/null
+++ b/autistdraw.c
@@ -0,0 +1,232 @@
+// <poll.h> might need this for sigset_t
+#define _XOPEN_SOURCE 600
+
+#include <stdio.h>
+#include <unistd.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <poll.h>
+#include <signal.h>
+
+#include <curses.h>
+#include "termo.h"
+
+#include "config.h"
+
+typedef struct app_data app_data_t;
+struct app_data
+{
+ termo_t *tk;
+
+ // Current attributes for the left mouse button
+ int current_attrs_left;
+ // Current attributes for the right mouse button
+ int current_attrs_right;
+};
+
+static int g_winch_pipe[2];
+
+static void
+display (const char *format, ...)
+{
+ va_list ap;
+
+ mvwhline (stdscr, 0, 0, A_REVERSE, COLS);
+ attron (A_REVERSE);
+
+ va_start (ap, format);
+ vw_printw (stdscr, format, ap);
+ va_end (ap);
+
+ attroff (A_REVERSE);
+ refresh ();
+}
+
+static void
+init_palette (app_data_t *app)
+{
+ start_color ();
+
+ // Also does init_pair (0, -1, -1);
+ use_default_colors ();
+ // Duplicate it for convenience.
+ init_pair (9, -1, -1);
+
+ // Add the basic 8 colors to the default pair. Once normally, once
+ // inverted to workaround VTE's inability to set a bright background.
+ for (int i = 0; i < 8; i++)
+ {
+ init_pair (1 + i, COLOR_WHITE, COLOR_BLACK + i);
+ init_pair (10 + i, COLOR_BLACK + i, COLOR_WHITE);
+ }
+
+ // This usually creates a solid black or white.
+ app->current_attrs_left =
+ app->current_attrs_right = COLOR_PAIR (9) | A_REVERSE | A_BOLD;
+}
+
+static void
+redraw (void)
+{
+ int i;
+
+ mvwhline (stdscr, 1, 0, A_REVERSE, COLS);
+ mvwhline (stdscr, 2, 0, A_REVERSE, COLS);
+
+ for (i = 0; i < COLS; i++)
+ {
+ int pair = (float) i / COLS * 9;
+ mvaddch (1, i, ' ' | COLOR_PAIR (pair));
+ mvaddch (2, i, ' ' | COLOR_PAIR (pair + 9) | A_REVERSE | A_BOLD);
+ }
+
+ display ("Choose a color from the palette and draw. "
+ "Press Escape or ^C to quit.");
+ refresh ();
+}
+
+static bool
+on_key (app_data_t *app, termo_key_t *key)
+{
+ if (key->type == TERMO_TYPE_KEYSYM
+ && key->code.sym == TERMO_SYM_ESCAPE)
+ return false;
+
+ if (key->type == TERMO_TYPE_KEY
+ && (key->modifiers & TERMO_KEYMOD_CTRL)
+ && (key->code.codepoint == 'C' || key->code.codepoint == 'c'))
+ return false;
+
+ if (key->type != TERMO_TYPE_MOUSE)
+ return true;
+
+ int line, col, button;
+ termo_mouse_event_t event;
+
+ termo_interpret_mouse (app->tk, key, &event, &button, &line, &col);
+ if (event != TERMO_MOUSE_PRESS && event != TERMO_MOUSE_DRAG)
+ return true;
+
+ int *attrs;
+ if (button == 1)
+ attrs = &app->current_attrs_left;
+ else if (button == 3)
+ attrs = &app->current_attrs_right;
+ else
+ return true;
+
+ chtype ch = mvwinch (stdscr, line, col);
+ if (line >= 3)
+ {
+ // Paste the attributes where the user clicked.
+ addch (' ' | *attrs);
+ refresh ();
+ }
+ else if (line > 0)
+ // Copy attributes from the pallete.
+ *attrs = ch & (A_COLOR | A_ATTRIBUTES);
+
+ return true;
+}
+
+static void
+winch_handler (int signum)
+{
+ (void) signum;
+ write (g_winch_pipe[1], "x", 1);
+}
+
+int
+main (int argc, char *argv[])
+{
+ (void) argc;
+ (void) argv;
+
+ TERMO_CHECK_VERSION;
+ setlocale (LC_CTYPE, "");
+
+ struct sigaction act;
+ act.sa_handler = winch_handler;
+ act.sa_flags = SA_RESTART;
+ sigemptyset (&act.sa_mask);
+
+ // Set up a self-pipe so that we can actually poll on SIGWINCH
+ if (sigaction (SIGWINCH, &act, NULL) || pipe (g_winch_pipe))
+ {
+ fprintf (stderr, "Cannot set up signal handler\n");
+ exit (EXIT_FAILURE);
+ }
+
+ termo_t *tk = termo_new (STDIN_FILENO, NULL, 0);
+ if (!tk)
+ {
+ fprintf (stderr, "Cannot allocate termo instance\n");
+ exit (EXIT_FAILURE);
+ }
+
+ termo_set_mouse_proto (tk, termo_guess_mouse_proto (tk));
+ termo_set_mouse_tracking_mode (tk, TERMO_MOUSE_TRACKING_DRAG);
+
+ // Set up curses for our drawing needs
+ if (!initscr () || nonl () == ERR || curs_set (0) == ERR)
+ {
+ fprintf (stderr, "Cannot initialize curses\n");
+ exit (EXIT_FAILURE);
+ }
+
+ app_data_t app;
+ memset (&app, 0, sizeof app);
+ app.tk = tk;
+
+ init_palette (&app);
+ redraw ();
+
+ termo_result_t ret;
+ termo_key_t key;
+
+ // We listen for mouse/key input and terminal resize events
+ struct pollfd fds[2] =
+ {
+ { .fd = STDIN_FILENO, .events = POLLIN },
+ { .fd = g_winch_pipe[0], .events = POLLIN },
+ };
+
+ // Run a simple event loop with poll()
+ int nextwait = -1;
+ bool running = true;
+ while (running)
+ {
+ if (!poll (fds, 2, nextwait))
+ if (termo_getkey_force (tk, &key) == TERMO_RES_KEY)
+ running &= on_key (&app, &key);
+
+ if (fds[1].revents & (POLLIN | POLLHUP | POLLERR))
+ {
+ char x;
+ read (fds[1].fd, &x, 1);
+
+ // The "official" simple and flicker-prone method of resizing
+ // the internal buffers of curses
+ endwin ();
+ refresh ();
+
+ redraw ();
+ }
+ if (fds[0].revents & (POLLIN | POLLHUP | POLLERR))
+ termo_advisereadable (tk);
+
+ while ((ret = termo_getkey (tk, &key)) == TERMO_RES_KEY)
+ running &= on_key (&app, &key);
+
+ nextwait = -1;
+ if (ret == TERMO_RES_AGAIN)
+ nextwait = termo_get_waittime (tk);
+ }
+
+ endwin ();
+ termo_destroy (tk);
+}
+
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 0000000..2e0b129
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,11 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define PROJECT_NAME "${CMAKE_PROJECT_NAME}"
+#define PROJECT_VERSION "${project_VERSION}"
+#define PROJECT_URL "${project_URL}"
+
+#cmakedefine HAVE_RESIZE_TERM
+
+#endif // ! CONFIG_H
+