commit
de8b897b31
|
@ -0,0 +1,4 @@
|
||||||
|
# Backup files
|
||||||
|
*.*~
|
||||||
|
# IDE project files
|
||||||
|
/CMakeLists.txt.user
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "termo"]
|
||||||
|
path = termo
|
||||||
|
url = git://github.com/pjanouch/termo.git
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue