Initial commit

Forked from termo's demo-draw.c
This commit is contained in:
Přemysl Eric Janouch 2014-10-22 23:58:25 +02:00
commit de8b897b31
7 changed files with 379 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Backup files
*.*~
# IDE project files
/CMakeLists.txt.user

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "termo"]
path = termo
url = git://github.com/pjanouch/termo.git

86
CMakeLists.txt Normal file
View File

@ -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)

14
LICENSE Normal file
View File

@ -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.

29
README Normal file
View File

@ -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

232
autistdraw.c Normal file
View File

@ -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);
}

11
config.h.in Normal file
View File

@ -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