Merge branch 'gui-branch'

gui-branch
an 2019-03-31 20:28:13 -04:00
commit a523d6f8d4
51 changed files with 1772 additions and 867 deletions

2
.gitignore vendored
View File

@ -1,6 +1,6 @@
/target
/out
*.res
**/*.rs.bk
Cargo.lock
perf.data*
*.bat

View File

@ -1,6 +1,6 @@
Some of the data contained in tests/data and benches/data is
Copyright © Bungie Software. I do not own them, but am permitted to
redistribute them. Everything else is:
redistribute them. Everything else is public domain, as stated:
To the extent possible under law, I, Alison Sanderson, have waived all
copyright and related or neighboring rights to this Document as described by

View File

@ -1,5 +1,7 @@
//! Library for utilities.
#[macro_use]
pub mod ffi;
#[macro_use]
pub mod err;
#[macro_use]
@ -13,6 +15,5 @@ pub mod file;
pub mod fixed;
pub mod image;
pub mod sound;
pub mod text;
// EOF

View File

@ -1,6 +1,6 @@
//! Binary data conversion utilities.
use crate::durandal::{err::*, text::mac_roman_conv};
use crate::durandal::err::*;
use std::{fmt, num::NonZeroU16};
#[doc(hidden)]
@ -505,14 +505,6 @@ impl fmt::Debug for OptU16
}
}
impl fmt::Debug for Ident
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "\"{}\"", mac_roman_conv(&self.0))
}
}
impl PartialEq<[u8; 4]> for Ident
{
#[inline]
@ -544,7 +536,7 @@ impl<'a> PartialEq<&'a [u8; 4]> for Ident
/// assert_eq!(&Ident(*b"POLY"), *b"POLY");
/// assert_eq!(&Ident(*b"POLY"), b"POLY");
/// ```
#[derive(Clone, Copy, Default, Eq, PartialEq)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize, serde::Deserialize))]
pub struct Ident(/** The individual bytes of this identifier. */ pub [u8; 4]);

View File

@ -58,6 +58,6 @@ pub fn crc32(b: &[u8], s: u32) -> u32
}
const ISO_3309_POLYNOMIAL: u32 = 0xEDB8_8320;
const ADLER32_MODULO: u32 = 65521;
const ADLER32_MODULO: u32 = 0xFFF1;
// EOF

87
source/durandal/ffi.rs Normal file
View File

@ -0,0 +1,87 @@
//! Foreign function interface utilities.
use crate::durandal::err::*;
pub use std::{ffi::*, os::raw::*, ptr::{null, null_mut}};
/// Creates a C string from a literal.
#[macro_export]
macro_rules! c_str {
($s:expr) => {concat!($s, "\0").as_ptr() as $crate::durandal::ffi::NT};
}
#[inline]
pub const fn null_void() -> *const c_void
{
null()
}
#[inline]
pub const fn null_mut_void() -> *mut c_void
{
null_mut()
}
impl CStringVec
{
/// Creates a new empty CStringVec.
#[inline]
pub fn new() -> Self
{
Self{sv: Vec::new(), cv: vec![null()]}
}
/// Creates a new `CStringVec` from an iterator.
#[inline]
pub fn new_from_iter<'a, I: Iterator<Item = &'a str>>(it: I)
-> ResultS<Self>
{
let mut v = Self::new();
for st in it {
v.push(CString::new(st)?);
}
Ok(v)
}
/// Pushes a new `CString`.
#[inline]
pub fn push(&mut self, st: CString)
{
self.cv.insert(self.cv.len() - 1, st.as_ptr());
self.sv.push(st);
}
/// Returns the FFI pointer.
#[inline]
pub fn as_ptr(&self) -> *const NT
{
self.cv.as_ptr()
}
/// Returns the FFI pointer mutably.
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut NT
{
self.cv.as_mut_ptr()
}
}
impl Default for CStringVec
{
#[inline]
fn default() -> Self {Self::new()}
}
/// An owned null-terminated string vector.
#[derive(Debug)]
pub struct CStringVec
{
sv: Vec<CString>,
cv: Vec<NT>,
}
/// A null-terminated byte string pointer.
pub type NT = *const c_char;
// EOF

View File

@ -1,6 +1,6 @@
use maraiah::{durandal::{err::*, file::*, image::*, sound::*},
marathon::{machdr, ppm, shp, snd, tga, wad, wav}};
use std::{fs, io};
use std::{fs, io, slice::from_ref};
fn make_tga(_opt: &Options, fname: &str, im: &impl Image) -> ResultS<()>
{
@ -133,13 +133,13 @@ fn main() -> ResultS<()>
{
use argparse::*;
let mut opt: Options = Default::default();
let mut opt = Options::default();
{
let mut ap = ArgumentParser::new();
macro_rules! arg {
($name:expr, $ref:expr, $type:expr, $desc:expr) => {
ap.refer(&mut $ref).add_option(&[$name], $type, $desc);
ap.refer(&mut $ref).add_option(from_ref(&$name), $type, $desc);
};
}

View File

@ -8,6 +8,7 @@ pub mod pict;
pub mod ppm;
pub mod shp;
pub mod snd;
pub mod text;
pub mod tga;
pub mod trm;
pub mod wad;

View File

@ -389,7 +389,7 @@ impl HuffmanTable
p += 1;
// check our symbol table for this one (quick tree check)
let count = u16::from(self.nums[i]);
let count = self.nums[i];
if i32::from(code) - i32::from(count) < i32::from(first) {
return Ok((i, self.syms[usize::from(index + code - first)]));

View File

@ -1,7 +1,7 @@
//! Structures used by Marathon's Map format.
use crate::{durandal::{bin::*, err::*, fixed::*, text::*},
marathon::xfer::TransferMode};
use crate::{durandal::{bin::*, err::*, fixed::*},
marathon::{text::*, xfer::TransferMode}};
use bitflags::bitflags;
/// Reads a `LightFunc` object.
@ -222,7 +222,7 @@ fn read_poly_inter(b: &[u8]) -> ResultS<Polygon>
}
Ok(Polygon{tex_flr, tex_cei, hei_flr, hei_cei, lit_flr, lit_cei, xfr_flr,
xfr_cei, ..Default::default()})
xfr_cei, ..Polygon::default()})
}
/// Reads a `POLY` chunk.
@ -751,7 +751,7 @@ pub struct Note
/// Static map information.
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
#[derive(Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Minf
{
pub texture_id: u16,

View File

@ -47,8 +47,8 @@ fn read_pm_header<'a>(b: &'a [u8],
}
let rle = pack_t == PackType::Default ||
pack_t == PackType::Rle16 && depth == Depth::Bits16 ||
pack_t == PackType::Rle32 && depth == Depth::Bits32;
pack_t == PackType::Rle16 && depth == Depth::_16 ||
pack_t == PackType::Rle32 && depth == Depth::_32;
let pitch = usize::from(pt_fl & 0x3FFF);
@ -61,7 +61,7 @@ fn read_pm_ind(mut im: Image8, b: &[u8], hdr: Header) -> ResultS<Image8>
let clut = ok!(hdr.clut, "no CLUT in indexed mode")?;
let mut p = 0;
if hdr.pitch < 8 && hdr.depth == Depth::Bits8 {
if hdr.pitch < 8 && hdr.depth == Depth::_8 {
// uncompressed 8-bit colormap indices
for _ in 0..im.h() {
for _ in 0..im.w() {
@ -79,7 +79,7 @@ fn read_pm_ind(mut im: Image8, b: &[u8], hdr: Header) -> ResultS<Image8>
for _ in 0..im.h() {
let (d, pp) = read_rle::<u8>(&b[p..], hdr.pitch)?;
let d = if hdr.depth < Depth::Bits8 {
let d = if hdr.depth < Depth::_8 {
expand_data(d, hdr.depth)?
} else {
d
@ -195,12 +195,12 @@ fn read_pm_area(im: Image8, b: &[u8], pack: bool, clip: bool)
let (b, hdr) = read_pm_header(&b[p..], pack, clip, &im)?;
match hdr.depth {
Depth::Bits1 |
Depth::Bits2 |
Depth::Bits4 |
Depth::Bits8 => read_pm_ind(im, b, hdr),
Depth::Bits16 => read_pm_16(im, b, hdr),
Depth::Bits32 => read_pm_32(im, b, hdr),
Depth::_1 |
Depth::_2 |
Depth::_4 |
Depth::_8 => read_pm_ind(im, b, hdr),
Depth::_16 => read_pm_16(im, b, hdr),
Depth::_32 => read_pm_32(im, b, hdr),
}
}
@ -442,25 +442,25 @@ impl ReadRleData for u8
fn expand_data(b: Vec<u8>, depth: Depth) -> ResultS<Vec<u8>>
{
let mut o = Vec::with_capacity(match depth {
Depth::Bits4 => b.len() * 2,
Depth::Bits2 => b.len() * 4,
Depth::Bits1 => b.len() * 8,
Depth::_4 => b.len() * 2,
Depth::_2 => b.len() * 4,
Depth::_1 => b.len() * 8,
_ => bail!("invalid bit depth"),
});
for ch in b {
match depth {
Depth::Bits4 => {
Depth::_4 => {
for i in (0..=1).rev() {
o.push(ch >> (i * 4) & 0xF_u8);
}
}
Depth::Bits2 => {
Depth::_2 => {
for i in (0..=3).rev() {
o.push(ch >> (i * 2) & 0x3_u8);
}
}
Depth::Bits1 => {
Depth::_1 => {
for i in (0..=7).rev() {
o.push(ch >> i & 0x1_u8);
}
@ -484,12 +484,12 @@ struct Header
c_enum! {
enum Depth: u16
{
Bits1 = 1,
Bits2 = 2,
Bits4 = 4,
Bits8 = 8,
Bits16 = 16,
Bits32 = 32,
_1 = 1,
_2 = 2,
_4 = 4,
_8 = 8,
_16 = 16,
_32 = 32,
}
}

View File

@ -1,4 +1,4 @@
//! Portable PixMap format images.
//! Portable Pixel Map format images.
use crate::durandal::{err::*, fixed::FixedLong, image::*};
use std::io;
@ -40,7 +40,7 @@ pub fn read_ppm(inp: &[u8]) -> ResultS<Image16>
st => break st
}
};
let st = unsafe {std::str::from_utf8_unchecked(&st)};
let st = unsafe {std::str::from_utf8_unchecked(st)};
let nu = u16::from_str_radix(st, 10)?;
Ok(nu)
};
@ -56,9 +56,9 @@ pub fn read_ppm(inp: &[u8]) -> ResultS<Image16>
let g = FixedLong::from_int(get_num()?.into());
let b = FixedLong::from_int(get_num()?.into());
let r = (r / depth * 65535).integ() as u16;
let g = (g / depth * 65535).integ() as u16;
let b = (b / depth * 65535).integ() as u16;
let r = (r / depth * 0xFFFF).integ() as u16;
let g = (g / depth * 0xFFFF).integ() as u16;
let b = (b / depth * 0xFFFF).integ() as u16;
im.cr.push(Color16::new(r, g, b));
}

View File

@ -1,7 +1,7 @@
//! Marathon Shapes format handling.
use crate::{durandal::{bin::*, err::*, fixed::*, image::*, text::*},
marathon::xfer::TransferMode};
use crate::{durandal::{bin::*, err::*, fixed::*, image::*},
marathon::{text::*, xfer::TransferMode}};
use bitflags::bitflags;
/// Reads a color from a color table into `clut`.

View File

@ -5,7 +5,7 @@
/// # Examples
///
/// ```
/// use maraiah::durandal::text::to_binsize;
/// use maraiah::marathon::text::to_binsize;
///
/// assert_eq!(to_binsize(5000), "5kB".to_string());
/// ```
@ -57,7 +57,7 @@ pub fn fuck_string(s: &[u8]) -> Vec<u8>
/// # Examples
///
/// ```
/// use maraiah::durandal::text::pascal_str;
/// use maraiah::marathon::text::pascal_str;
///
/// assert_eq!(pascal_str(b"\x0bhello world"), b"hello world"[..].into());
/// assert_eq!(pascal_str(b"\x0chello world"), None);
@ -74,7 +74,7 @@ pub fn pascal_str(b: &[u8]) -> Option<&[u8]>
/// # Examples
///
/// ```
/// use maraiah::durandal::text::mac_roman_conv;
/// use maraiah::marathon::text::mac_roman_conv;
///
/// assert_eq!(mac_roman_conv(b"p\x8cth"), "påth");
/// assert_eq!(mac_roman_conv(b"I\xd5ve"), "Ive");
@ -99,7 +99,7 @@ pub fn mac_roman_conv(s: &[u8]) -> String
/// # Examples
///
/// ```
/// use maraiah::durandal::text::mac_roman_cstr;
/// use maraiah::marathon::text::mac_roman_cstr;
///
/// assert_eq!(mac_roman_cstr(b"I\xd5ve awaken\0ed"), "Ive awaken");
/// assert_eq!(mac_roman_cstr(b"I\xd5ve awaken\0"), "Ive awaken");

View File

@ -1,6 +1,6 @@
//! Structures used by Marathon's Map format's terminal definitions.
use crate::durandal::{bin::*, err::*, text::*};
use crate::{durandal::{bin::*, err::*}, marathon::text::*};
use bitflags::bitflags;
/// Reads an `InterGroup`.

View File

@ -1,7 +1,7 @@
//! Marathon Wad format handling.
use crate::{durandal::{bin::*, err::*, image, text::mac_roman_cstr},
marathon::{map, phy, pict, trm}};
use crate::{durandal::{bin::*, err::*, image},
marathon::{map, phy, pict, text::mac_roman_cstr, trm}};
use std::collections::BTreeMap;
/// Reads all chunks in an entry.

View File

@ -2,5 +2,6 @@
pub mod color;
pub mod draw;
pub mod editor;
// EOF

View File

@ -2,7 +2,7 @@
use crate::durandal::image::Color16;
pub const CR_RED: Color16 = Color16::new(0xFFFF, 0, 0);
pub const CR_DARK_RED: Color16 = Color16::new(0x4700, 0, 0);
pub const RED: Color16 = Color16::new(0xFFFF, 0, 0);
pub const DARK_RED: Color16 = Color16::new(0x4700, 0, 0);
// EOF

View File

@ -24,17 +24,23 @@ pub trait DrawArea
/// The height of the entire area.
fn h(&self) -> Coord;
/// Fills the entire screen with `cr`.
fn clear(&self, cr: impl Color);
/// Fills the entire screen with `cr`. Will also default all settings.
fn clear(&mut self, cr: impl Color);
/// Changes the width for lines. The default is `1`.
fn line_width(&mut self, width: u8);
/// Draws a line from `p1` to `p2` with color `cr`.
fn line(&mut self, p1: Point, p2: Point, cr: impl Color);
/// Draws a rectangle `rect` of color `cr`.
fn rect(&self, rect: Rect, cr: impl Color);
fn rect(&mut self, rect: Rect, cr: impl Color);
/// Draws the Unicode `text` at `pos` stroked with color `cr`.
fn text(&self, pos: Point, text: &str, cr: impl Color);
fn text(&mut self, pos: Point, text: &str, cr: impl Color);
/// Draws `im` at `pos`, starting from the top left column.
fn image(&self, pos: Point, im: &Self::NativeImage);
fn image(&mut self, pos: Point, im: &Self::NativeImage);
}
/// A type capable of representing any coordinate on any axis.

View File

@ -0,0 +1,90 @@
//! Main map editor module.
//!
//! The entry point is responsible for maintaining the lifetime of the editor
//! and human interactions with it, but is otherwise not permitted to directly
//! edit it.
mod block;
mod state;
use crate::durandal::image::*;
use super::{color, draw::*};
impl MapEditor
{
/// Opens the editor with a new empty map.
#[inline]
pub fn open_new(&mut self)
{
self.map = Some(state::OpenMap::default());
}
/// Opens the editor with an existing map.
pub fn open_buf(&mut self, b: &[u8])
{
self.map = Some(state::OpenMap::open_buf(b));
}
/// Draws the screen for this editor state.
pub fn draw<D, I>(&self, d: &mut D, im: &I)
where D: DrawArea<NativeImage = I>,
I: CacheImage
{
let dw = d.w();
let dh = d.h();
let iw = im.w();
let ih = im.h();
match &self.map {
None => {
let tx_top = "Map Required To Proceed";
let tx_bot = "CAS.qterm//CyberAcme Systems Inc.";
d.clear(Color16::new(0, 0, 0));
d.image((dw / 2 - iw / 2, dh / 2 - ih / 2), im);
d.rect(Rect{x: 0, y: 0, w: dw, h: 18}, color::DARK_RED);
d.text((4, 14), tx_top, color::RED);
d.rect(Rect{x: 0, y: dh - 18, w: dw, h: 18}, color::DARK_RED);
d.text((4, dh - 4), tx_bot, color::RED);
}
Some(st) => {
let text = &format!("tool: {:?}", st.tool());
d.clear(Color16::new(0, 0, 0));
d.text((dw/2, dh/2), text, color::RED);
}
}
}
/// Returns `true` if `self` is closed.
#[inline]
pub fn is_closed(&self) -> bool
{
self.map.is_none()
}
/// Returns `true` if `self` is opened.
#[inline]
pub fn is_opened(&self) -> bool
{
self.map.is_some()
}
}
impl Default for MapEditor
{
#[inline]
fn default() -> Self {Self{map: None}}
}
/// An entire map editor, which may be opened or closed. Holds state which
/// outlives the opened map state.
pub struct MapEditor
{
map: Option<state::OpenMap>,
}
// EOF

View File

@ -0,0 +1,19 @@
use crate::marathon::map;
impl Default for Block
{
#[inline]
fn default() -> Self
{
Self{info: map::Minf::default()}
}
}
/// Copyable, versioned map state.
#[derive(Clone, Debug)]
pub(super) struct Block
{
pub(super) info: map::Minf,
}
// EOF

View File

@ -0,0 +1,82 @@
//! Map editor state.
use super::block;
use crate::marathon::{machdr, wad};
impl OpenMap
{
pub(super) fn open_buf(b: &[u8]) -> Self
{
// TODO: handle errors gracefully
let b = &b[machdr::try_mac_header(b)..];
let wad = wad::read_wad(b).unwrap();
let ent = wad.entries.iter().nth(0).unwrap().1;
let info = ent.chunks.iter().find_map(|cnk| {
match cnk {
wad::Chunk::Minf(info) => Some(info),
_ => None,
}
}).unwrap().clone();
let block = block::Block{info};
dbg!(&block);
Self{blocks: vec![block],
tools: Self::default_tools()}
}
pub(super) fn cur_block(&self) -> &block::Block
{
self.blocks.last().unwrap()
}
pub(super) fn cur_block_mut(&mut self) -> &mut block::Block
{
self.blocks.last_mut().unwrap()
}
pub(super) fn tool(&self) -> &Tool
{
&self.tools.0
}
pub(super) fn set_tool(&mut self, t: Tool) -> &Tool
{
self.tools.1 = self.tools.0.clone();
self.tools.0 = t;
&self.tools.1
}
const fn default_tools() -> (Tool, Tool) {(Tool::Points, Tool::Lines)}
}
impl Default for OpenMap
{
#[inline]
fn default() -> Self
{
Self{blocks: vec![block::Block::default()],
tools: Self::default_tools()}
}
}
/// The state of an opened map editor.
pub(super) struct OpenMap
{
blocks: Vec<block::Block>,
tools: (Tool, Tool),
}
/// A tool in the map editor.
#[derive(Clone, Debug)]
pub(super) enum Tool
{
Points,
Lines,
Polygons,
}
// EOF

View File

@ -8,23 +8,17 @@ edition = "2018"
build = "build.rs"
[dependencies]
maraiah = {path = "../.."}
# note: these have to be updated all at once, check the gtk crate for versions
atk = "0.6"
cairo-rs = "0.6"
cairo-sys-rs = "0.8"
gdk = "0.10"
gdk-pixbuf = "0.6"
gio = "0.6"
gio-sys = "0.8"
glib = "0.7"
glib-sys = "0.8"
gobject-sys = "0.8"
gtk = {version = "0.6", features = ["v3_16"]}
gtk-sys = "0.8"
pango = "0.6"
pango-sys = "0.8"
atk-sys = "0.8"
cairo-sys-rs = "0.8"
gdk-pixbuf-sys = "0.8"
gdk-sys = "0.8"
gio-sys = "0.8"
glib-sys = "0.8"
gobject-sys = "0.8"
gtk-sys = {version = "0.8", features = ["v3_16"]}
maraiah = {path = "../.."}
memmap = "0.7"
pango-sys = "0.8"
[[bin]]
name = "tycho"

View File

@ -1,13 +1,48 @@
use std::process::Command;
use std::{env, fs, io, path::Path, process::Command};
fn main()
fn io_err(st: &'static str) -> io::Error
{
println!("cargo:rerun-if-changed=data");
io::Error::new(io::ErrorKind::Other, st)
}
Command::new("glib-compile-resources").arg("data/tycho_res.xml")
.arg("--target=data/tycho.res")
.status()
.unwrap();
fn traverse_dir(path: &Path) -> io::Result<()>
{
for ent in fs::read_dir(path)? {
let path = ent?.path();
if path.is_dir() {
traverse_dir(&path)?;
} else {
if let Some(path) = path.to_str() {
println!("cargo:rerun-if-changed={}", path);
} else {
return Err(io_err("failed to convert path"));
}
}
}
Ok(())
}
fn main() -> io::Result<()>
{
let out_file = env::var("OUT_DIR").unwrap();
let out_file = format!("--target={}/resources", out_file);
// traverse each file in the data directory, because cargo won't do this
traverse_dir(Path::new("data"))?;
let o = Command::new("glib-compile-resources").arg("data/resources.xml")
.arg(out_file)
.output()
.unwrap();
if !o.status.success() {
dbg!(o);
Err(io_err("failed to compile resources"))
} else {
Ok(())
}
}
// EOF

Binary file not shown.

After

Width:  |  Height:  |  Size: 805 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 972 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 538 B

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<!-- Toplevel namespace. Contains UI and helpers. -->
<gresource prefix="/net/greyserv/maraiah/tycho">
<file compressed="true" alias="tycho1.png">data/misc/tycho1.png</file>
<file compressed="true" alias="tycho2.png">data/misc/tycho2.png</file>
<file compressed="true" alias="css">data/styles.css</file>
<file compressed="true" preprocess="xml-stripblanks" alias="ui">data/ui.xml</file>
</gresource>
<!-- Icons, color. We can't make highcolor yet. -->
<gresource prefix="/net/greyserv/maraiah/tycho/icons/48x48/actions">
<file compressed="true" alias="tycho-polys.png">data/color/polygons.png</file>
<file compressed="true" alias="tycho-lines.png">data/color/lines.png</file>
<file compressed="true" alias="tycho-points.png">data/color/points.png</file>
</gresource>
<gresource prefix="/net/greyserv/maraiah/tycho/icons/48x48/apps">
<file compressed="true" alias="tycho-map.png">data/color/map.png</file>
</gresource>
</gresources>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
/* EOF */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/net/greyserv/maraiah/tycho">
<file compressed="true" alias="tycho1">data/tycho1.png</file>
<file compressed="true" alias="tycho2">data/tycho2.png</file>
<file compressed="true" alias="polys">data/polys.png</file>
<file compressed="true" alias="lines">data/lines.png</file>
<file compressed="true" alias="points">data/points.png</file>
<file compressed="true" preprocess="xml-stripblanks" alias="ui">data/tycho.xml</file>
</gresource>
</gresources>

File diff suppressed because it is too large Load Diff

View File

@ -1,40 +0,0 @@
use maraiah::{durandal::image::*,
marathon::*,
rozinante::{color::*, draw::*}};
pub fn draw_map_none<D, I>(d: &D, im: &I)
where D: DrawArea<NativeImage = I>,
I: CacheImage
{
d.clear(Color16::new(0, 0, 0));
d.image((d.w() / 2 - im.w() / 2, d.h() / 2 - im.h() / 2), im);
d.rect(Rect{x: 0, y: 0, w: d.w(), h: 18}, CR_DARK_RED);
d.text((4, 14), "Map Required To Proceed", CR_RED);
d.rect(Rect{x: 0, y: d.h() - 18, w: d.w(), h: 18}, CR_DARK_RED);
d.text((4, d.h() - 4), "CAS.qterm//CyberAcme Systems Inc.", CR_RED);
}
pub fn new_map() -> MapState
{
let info = Default::default();
let ed = EditorState{};
let ma = MapState{ed, info};
ma
}
pub struct EditorState
{
}
pub struct MapState
{
ed: EditorState,
info: map::Minf,
}
// EOF

194
source/tycho/interfaces.rs Normal file
View File

@ -0,0 +1,194 @@
//! Implemented interfaces for Rozinante.
use cairo_sys::*;
use gdk_pixbuf_sys::*;
use gdk_sys::*;
use gobject_sys::*;
use gtk_sys::*;
use maraiah::{c_str,
durandal::{ffi, image::*},
rozinante::{draw::*, editor}};
/// Converts a `Color` to a `f64` triple.
fn flt_color(cr: impl Color) -> (f64, f64, f64)
{
fn flt_color(n: u16) -> f64 {f64::from(n) / f64::from(u16::max_value())}
(flt_color(cr.r()), flt_color(cr.g()), flt_color(cr.b()))
}
impl CacheImage for CrImage
{
fn w(&self) -> Coord {unsafe {gdk_pixbuf_get_width(self.0) as Coord}}
fn h(&self) -> Coord {unsafe {gdk_pixbuf_get_height(self.0) as Coord}}
}
impl CrDrawArea
{
pub const fn new(ctx: *mut cairo_t, w: f64, h: f64) -> Self
{
CrDrawArea{ctx, w: w as Coord, h: h as Coord}
}
}
impl DrawArea for CrDrawArea
{
type NativeImage = CrImage;
fn w(&self) -> Coord {self.w}
fn h(&self) -> Coord {self.h}
fn clear(&mut self, cr: impl Color)
{
self.rect(Rect{x: 0, y: 0, w: self.w(), h: self.h()}, cr);
let sl = FONT_SLANT_NORMAL;
let wt = FONT_WEIGHT_NORMAL;
unsafe {
cairo_select_font_face(self.ctx, c_str!("Monospace"), sl, wt);
cairo_set_font_size(self.ctx, 14.0);
cairo_set_line_width(self.ctx, 1.0);
}
}
fn line_width(&mut self, width: u8)
{
let width = f64::from(width);
unsafe {
cairo_set_line_width(self.ctx, width);
}
}
fn line(&mut self, p1: Point, p2: Point, cr: impl Color)
{
let (r, g, b) = flt_color(cr);
let x1 = f64::from(p1.0);
let y1 = f64::from(p1.1);
let x2 = f64::from(p2.0);
let y2 = f64::from(p2.1);
unsafe {
cairo_set_source_rgb(self.ctx, r, g, b);
cairo_move_to(self.ctx, x1, y1);
cairo_line_to(self.ctx, x2, y2);
cairo_stroke(self.ctx);
}
}
fn rect(&mut self, rect: Rect, cr: impl Color)
{
let px = f64::from(rect.x);
let py = f64::from(rect.y);
let sx = f64::from(rect.w);
let sy = f64::from(rect.h);
let (r, g, b) = flt_color(cr);
unsafe {
cairo_set_source_rgb(self.ctx, r, g, b);
cairo_rectangle(self.ctx, px, py, sx, sy);
cairo_fill(self.ctx);
}
}
fn text(&mut self, pos: Point, text: &str, cr: impl Color)
{
let (r, g, b) = flt_color(cr);
let x = f64::from(pos.0);
let y = f64::from(pos.1);
let text = ffi::CString::new(text).unwrap();
unsafe {
cairo_set_source_rgb(self.ctx, r, g, b);
cairo_move_to(self.ctx, x, y);
cairo_show_text(self.ctx, text.as_ptr());
}
}
fn image(&mut self, pos: Point, im: &Self::NativeImage)
{
let x = f64::from(pos.0);
let y = f64::from(pos.1);
unsafe {
gdk_cairo_set_source_pixbuf(self.ctx, im.0, x, y);
cairo_paint(self.ctx);
}
}
}
impl MapEditor
{
/// Propagates updated information to widgets.
pub fn cause_update(&mut self)
{
unsafe {gtk_widget_queue_draw(self.draw);}
}
/// Sets the drawing area widget.
pub fn set_draw(&mut self, draw: *mut GtkWidget)
{
self.draw = draw;
unsafe {g_object_ref(self.draw as _);}
}
}
impl Default for MapEditor
{
fn default() -> Self
{
Self{edit: editor::MapEditor::default(),
draw: ffi::null_mut()}
}
}
impl Drop for MapEditor
{
fn drop(&mut self)
{
unsafe {g_object_unref(self.draw as _);}
}
}
impl std::ops::Deref for MapEditor
{
type Target = editor::MapEditor;
#[inline]
fn deref(&self) -> &Self::Target {&self.edit}
}
impl std::ops::DerefMut for MapEditor
{
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {&mut self.edit}
}
/// An image for a `CrDrawArea`.
pub struct CrImage(pub *const GdkPixbuf);
/// A `DrawArea` for a Cairo surface.
pub struct CrDrawArea
{
ctx: *mut cairo_t,
w: Coord,
h: Coord,
}
/// Specialized map editor which has callbacks for frontend purposes.
pub struct MapEditor
{
edit: editor::MapEditor,
draw: *mut GtkWidget,
}
/// A runtime reference to the map editor.
pub type MapEditorRef = std::cell::RefCell<MapEditor>;
// EOF

View File

@ -1,127 +1,490 @@
mod hiddenprotocol;
mod noroom;
mod interfaces;
use crate::{hiddenprotocol::*, noroom::*};
use gio::prelude::*;
use gtk::prelude::*;
use maraiah::durandal::err::*;
use crate::interfaces::*;
use gdk_pixbuf_sys::*;
use gdk_sys::*;
use gio_sys::*;
use glib_sys::*;
use gobject_sys::*;
use gtk_sys::*;
use maraiah::{c_str, durandal::ffi};
use std::{cell::RefCell, marker::PhantomData, rc::Rc};
fn hide_on_delete(win: &gtk::Window, _: &gdk::Event) -> Inhibit
const ACTIVATE: ffi::NT = c_str!("activate");
const APP_ID: ffi::NT = c_str!("net.greyserv.maraiah.tycho");
const DELETE_EVENT: ffi::NT = c_str!("delete-event");
const DESTROY: ffi::NT = c_str!("destroy");
const DRAW: ffi::NT = c_str!("draw");
const IM_ABOUT: ffi::NT = c_str!("/net/greyserv/maraiah/tycho/tycho2.png");
const IM_NOMAP: ffi::NT = c_str!("/net/greyserv/maraiah/tycho/tycho1.png");
const PATH_BUILDER: ffi::NT = c_str!("/net/greyserv/maraiah/tycho/ui");
const PATH_CSS: ffi::NT = c_str!("/net/greyserv/maraiah/tycho/css");
/// Called when the application activates in order to set everything up.
unsafe extern "C" fn app_activate(app: *mut GtkApplication, edit: gpointer)
{
win.hide();
Inhibit(true)
// this ref will be cloned around a bit, but will ultimately be dropped at
// the end of this function.
let edit = Rc::from_raw(edit as *const MapEditorRef);
setup_css();
let b = Refc::new(gtk_builder_new_from_resource(PATH_BUILDER));
setup_draw_area(&b, edit.clone());
setup_win_map_view(&b);
setup_win_map_tools(&b);
setup_win_map_prop(&b);
setup_about_dlg(&b);
setup_win_main(&b, app, edit.clone());
}
fn mk_draw_area(b: &gtk::Builder)
/// Sets up the map view window's drawing area.
unsafe fn setup_draw_area(b: &Refc<GtkBuilder>, edit: Rc<MapEditorRef>)
{
let area: gtk::DrawingArea = get_obj(b, "draw-area");
let ax: gtk::Adjustment = get_obj(b, "adj-map-horz");
let ay: gtk::Adjustment = get_obj(b, "adj-map-vert");
let im = CairoPixbuf(load_img("/net/greyserv/maraiah/tycho/tycho1"));
area.connect_draw(move |area, cr| {
let w = f64::from(area.get_allocated_width());
let h = f64::from(area.get_allocated_height());
ax.set_lower(0.0);
ax.set_upper(w);
ay.set_lower(0.0);
ay.set_upper(h);
let d = CairoDrawArea::new(cr.clone(), w, h);
draw_map_none(&d, &im);
Inhibit(true)
});
}
fn run_app(app: &gtk::Application)
{
let b = &gtk::Builder::new_from_resource("/net/greyserv/maraiah/tycho/ui");
let app_ = app.clone();
let btn: gtk::MenuItem = get_obj(b, "btn-quit");
btn.connect_activate(move |_| app_.quit());
let btn: gtk::MenuItem = get_obj(b, "btn-about");
let win: gtk::AboutDialog = get_obj(b, "win-about");
btn.connect_activate(move |_| {win.run(); win.hide();});
let btn: gtk::MenuItem = get_obj(b, "btn-show-map-view");
let win: gtk::Window = get_obj(b, "win-map-view");
win.connect_delete_event(hide_on_delete);
btn.connect_activate(move |_| win.show_all());
let btn: gtk::MenuItem = get_obj(b, "btn-show-map-tools");
let win: gtk::Window = get_obj(b, "win-map-tools");
win.connect_delete_event(hide_on_delete);
btn.connect_activate(move |_| win.show_all());
let btn: gtk::MenuItem = get_obj(b, "btn-show-map-prop");
let win: gtk::Window = get_obj(b, "win-map-prop");
win.connect_delete_event(hide_on_delete);
btn.connect_activate(move |_| win.show_all());
mk_draw_area(b);
let win: gtk::AboutDialog = get_obj(b, "win-about");
win.set_authors(&env!("CARGO_PKG_AUTHORS").split(';').collect::<Vec<_>>());
win.set_version(env!("CARGO_PKG_VERSION"));
win.set_website(env!("CARGO_PKG_HOMEPAGE"));
win.set_logo(&load_img("/net/greyserv/maraiah/tycho/tycho2"));
let win: gtk::Window = get_obj(b, "win-main");
win.set_application(app);
win.show_all();
}
fn load_img(path: &'static str) -> gdk_pixbuf::Pixbuf
{
gdk_pixbuf::Pixbuf::new_from_resource(path).unwrap()
}
fn get_obj<T>(b: &gtk::Builder, name: &str) -> T
where T: glib::object::IsA<glib::object::Object>
{
b.get_object(name).unwrap()
}
fn main() -> ResultS<()>
{
// get jacked, punk. opaque data structures are for nerds.
const RESOURCE_DATA: &[u8] = include_bytes!("data/tycho.res");
let mut static_resource =
gio_sys::GStaticResource{data: RESOURCE_DATA.as_ptr(),
data_len: RESOURCE_DATA.len(),
resource: std::ptr::null_mut(),
next: std::ptr::null_mut(),
padding: std::ptr::null_mut()};
unsafe {
gio_sys::g_static_resource_init(&mut static_resource);
/// All of the state necessary for the drawing area.
struct RenderState
{
im_nomap: Refc<'static, GdkPixbuf>,
ax: Refc<'static, GtkAdjustment>,
ay: Refc<'static, GtkAdjustment>,
edit: Rc<MapEditorRef>,
}
let app = gtk::Application::new("net.greyserv.maraiah.tycho",
gio::ApplicationFlags::empty())?;
/// Callback to finalize the drawing area.
unsafe extern "C" fn c_done(_: *mut GtkWidget, rend: gpointer)
{
Box::from_raw(rend as *mut RenderState);
}
app.connect_activate(run_app);
/// Callback to draw on the drawing area.
unsafe extern "C" fn c_draw(wid: *mut GtkWidget,
ctx: *mut cairo_sys::cairo_t,
rend: gpointer)
-> gboolean
{
let rend = &mut *(rend as *mut RenderState);
let ret = if app.run(&[]) == 0 {
Ok(())
let w = f64::from(gtk_widget_get_allocated_width(wid));
let h = f64::from(gtk_widget_get_allocated_height(wid));
gtk_adjustment_set_lower(*rend.ax, 0.0);
gtk_adjustment_set_upper(*rend.ax, w);
gtk_adjustment_set_lower(*rend.ay, 0.0);
gtk_adjustment_set_upper(*rend.ay, h);
let im = CrImage(*rend.im_nomap);
let mut dr = CrDrawArea::new(ctx, w, h);
rend.edit.borrow().draw(&mut dr, &im);
1
}
let wid = get_obj::<GtkDrawingArea>(b, c_str!("draw-area"));
edit.borrow_mut().set_draw(wid as _);
// get all of the necessary state and related objects
let ax = Refc::new(get_obj::<GtkAdjustment>(b, c_str!("adj-map-horz")));
let ay = Refc::new(get_obj::<GtkAdjustment>(b, c_str!("adj-map-vert")));
g_object_ref(*ax as _);
g_object_ref(*ay as _);
let im_nomap = Refc::new(load_img(IM_NOMAP));
let rend = RenderState{im_nomap, ax, ay, edit};
let rend = Box::into_raw(Box::new(rend));
connect(wid, DESTROY, c_done as _, rend);
connect(wid, DRAW, c_draw as _, rend);
}
/// Sets up the map view window.
unsafe fn setup_win_map_view(b: &Refc<GtkBuilder>)
{
let win = get_obj::<GtkWindow >(b, c_str!("win-map-view"));
let btn = get_obj::<GtkMenuItem>(b, c_str!("btn-show-map-view"));
connect_hide(win);
connect_show(btn, win);
}
/// Sets up the map tools window.
unsafe fn setup_win_map_tools(b: &Refc<GtkBuilder>)
{
let win = get_obj::<GtkWindow >(b, c_str!("win-map-tools"));
let btn = get_obj::<GtkMenuItem>(b, c_str!("btn-show-map-tools"));
connect_hide(win);
connect_show(btn, win);
}
/// Sets up the map properties window.
unsafe fn setup_win_map_prop(b: &Refc<GtkBuilder>)
{
let win = get_obj::<GtkWindow >(b, c_str!("win-map-prop"));
let btn = get_obj::<GtkMenuItem>(b, c_str!("btn-show-map-prop"));
connect_hide(win);
connect_show(btn, win);
}
/// Sets up the about dialogue.
unsafe fn setup_about_dlg(b: &Refc<GtkBuilder>)
{
/// Callback to show the dialogue when the "About" button is pressed, and
/// hide it when the "Close" button is pressed on it.
unsafe extern "C" fn c_show_act(_: *mut GtkWidget, dlg: gpointer)
{
gtk_dialog_run(dlg as _);
gtk_widget_hide(dlg as _);
}
let dlg = get_obj::<GtkAboutDialog>(b, c_str!("dlg-about"));
let btn = get_obj::<GtkMenuItem >(b, c_str!("btn-about"));
let it = env!("CARGO_PKG_AUTHORS").split(';');
let mut v = ffi::CStringVec::new_from_iter(it).unwrap();
let img = Refc::new(load_img(IM_ABOUT));
gtk_about_dialog_set_authors(dlg, v.as_mut_ptr());
gtk_about_dialog_set_version(dlg, c_str!(env!("CARGO_PKG_VERSION")));
gtk_about_dialog_set_website(dlg, c_str!(env!("CARGO_PKG_HOMEPAGE")));
gtk_about_dialog_set_logo(dlg, *img);
connect_hide(dlg);
connect(btn, ACTIVATE, c_show_act as _, dlg);
}
/// Sets up explicit window finalization for the main window.
unsafe fn setup_explicit_drop(b: &Refc<GtkBuilder>, win: *mut GtkWindow)
{
/// Callback to explicitly finalize all windows on exit.
unsafe extern "C" fn c_done(_: *mut GtkWidget, exp_del: gpointer)
{
let exp_del = Box::from_raw(exp_del as *mut Vec<*mut GtkWindow>);
for win in *exp_del {
gtk_widget_destroy(win as _);
}
}
// we need to explicitly drop other windows on exit, which means we need to
// create a list of them to send to the callback
let mut exp_del = Vec::new();
// so, we get all of the objects from the builder, and iterate through them
let head = gtk_builder_get_objects(**b);
let mut lst = &*head;
loop {
let obj = lst.data as *mut GObject;
// while this is well-defined, it is a weird way of doing it, because
// this exact method of checking types isn't fully documented. we can't
// use the macros for this functionality because we're not using C, so we
// use the underlying function calls. again, get jacked, punk.
if g_type_check_instance_is_a(obj as _, gtk_window_get_type()) != 0 {
let owin = obj as *mut GtkWindow;
if owin != win {
exp_del.push(owin);
}
}
let nx = lst.next;
if nx != ffi::null_mut() {
lst = &*nx;
} else {
break;
}
}
g_slist_free(head);
let exp_del = Box::into_raw(Box::new(exp_del));
connect(win, DESTROY, c_done as _, exp_del);
}
/// Sets up the main menu window.
unsafe fn setup_win_main(b: &Refc<GtkBuilder>,
app: *mut GtkApplication,
edit: Rc<MapEditorRef>)
{
/// Callback to close the window when the "Quit" button is pressed.
unsafe extern "C" fn c_quit_act(_: *mut GtkWidget, win: gpointer)
{
gtk_window_close(win as _);
}
/// Callback to create a new map when the "New" button is pressed.
unsafe extern "C" fn c_new_act(_: *mut GtkWidget, edit: gpointer)
{
let edit = &*(edit as *const MapEditorRef);
let mut edit = edit.borrow_mut();
if edit.is_opened() {
let titl = c_str!("Confirm");
let text = c_str!("Are you sure you want to create a new project? \
Unsaved data may be lost.");
if !run_ok_cancel_dlg(titl, text) {
return;
}
}
edit.open_new();
edit.cause_update();
}
/// Callback to open an existing map when the "Open" button is pressed.
unsafe extern "C" fn c_open_act(_: *mut GtkWidget, edit: gpointer)
{
let edit = &*(edit as *const MapEditorRef);
let mut edit = edit.borrow_mut();
if edit.is_opened() {
let titl = c_str!("Confirm");
let text = c_str!("Are you sure you want to open this project? \
Unsaved data may be lost.");
if !run_ok_cancel_dlg(titl, text) {
return;
}
}
if let Some(path) = run_file_chooser_open() {
// TODO: handle errors gracefully
let fp = std::fs::File::open(&path).unwrap();
let mm = memmap::Mmap::map(&fp).unwrap();
edit.open_buf(&mm);
edit.cause_update();
}
}
// set up main window
let win = get_obj::<GtkWindow>(b, c_str!("win-main"));
setup_explicit_drop(b, win);
gtk_window_set_application(win, app);
gtk_widget_show_all(win as _);
// set up buttons
let btn = get_obj::<GtkMenuItem>(b, c_str!("btn-quit"));
connect(btn, ACTIVATE, c_quit_act as _, win);
let btn = get_obj::<GtkMenuItem>(b, c_str!("btn-new"));
connect(btn, ACTIVATE, c_new_act as _, connect_ref(btn, edit.clone()));
let btn = get_obj::<GtkMenuItem>(b, c_str!("btn-open"));
connect(btn, ACTIVATE, c_open_act as _, connect_ref(btn, edit.clone()));
}
/// Sets up the CSS styling providers.
unsafe fn setup_css()
{
let css = Refc::new(gtk_css_provider_new());
gtk_css_provider_load_from_resource(*css, PATH_CSS);
let scr = gdk_screen_get_default();
let pri = GTK_STYLE_PROVIDER_PRIORITY_APPLICATION as u32;
gtk_style_context_add_provider_for_screen(scr, *css as _, pri);
}
/// Runs a modal OK/Cancel dialogue.
unsafe fn run_ok_cancel_dlg(title: ffi::NT, text: ffi::NT) -> bool
{
let flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
let dlg = gtk_dialog_new_with_buttons(title,
ffi::null_mut(),
flags,
c_str!("_OK"),
GTK_RESPONSE_ACCEPT,
c_str!("_Cancel"),
GTK_RESPONSE_REJECT,
ffi::null_mut_void());
let area = gtk_dialog_get_content_area(dlg as _);
let labl = gtk_label_new(text);
gtk_container_add(area as _, labl);
gtk_widget_show_all(area as _);
let res = gtk_dialog_run(dlg as _);
gtk_widget_destroy(dlg);
res == GTK_RESPONSE_ACCEPT
}
/// Runs a modal Open File dialogue.
unsafe fn run_file_chooser_open() -> Option<String>
{
let action = GTK_FILE_CHOOSER_ACTION_OPEN;
let dlg = gtk_file_chooser_dialog_new(c_str!("Open File"),
ffi::null_mut(),
action,
c_str!("_Cancel"),
GTK_RESPONSE_CANCEL,
c_str!("_Open"),
GTK_RESPONSE_ACCEPT,
ffi::null_mut_void());
let res = gtk_dialog_run(dlg as _);
let ret = if res == GTK_RESPONSE_ACCEPT {
let fna = gtk_file_chooser_get_filename(dlg as _);
let own = ffi::CStr::from_ptr(fna);
let own = own.to_str().ok()?.to_owned();
g_free(fna as _);
Some(own)
} else {
Err(err_msg("bad return"))
None
};
unsafe {
gio_sys::g_static_resource_fini(&mut static_resource);
}
gtk_widget_destroy(dlg);
ret
}
/// Connects a handler that hides a toplevel widget when deleted.
unsafe fn connect_hide<T>(wid: *mut T)
{
/// Callback to hide the widget.
unsafe extern "C" fn c_hide_del(wid: *mut GtkWidget,
_: *mut GdkEvent,
_: gpointer)
{
gtk_widget_hide(wid);
}
connect(wid, DELETE_EVENT, c_hide_del as _, ffi::null_void());
}
/// Connects a handler that shows a widget when activated.
unsafe fn connect_show<T, U>(btn: *mut T, wid: *mut U)
{
/// Callback to show the widget.
unsafe extern "C" fn c_show_act(_: *mut GtkWidget, wid: gpointer)
{
gtk_widget_show_all(wid as _);
}
connect(btn, ACTIVATE, c_show_act as _, wid);
}
/// Connects a reference-counted object to a widget.
unsafe fn connect_ref<T, U>(obj: *mut T, rc: Rc<U>) -> *const U
{
/// Callback to finalize the reference.
unsafe extern "C" fn c_done(_: *mut GtkWidget, edit: gpointer)
{
Rc::from_raw(edit as *const MapEditorRef);
}
let ptr = Rc::into_raw(rc);
connect(obj, DESTROY, c_done as _, ptr);
ptr
}
/// Gets an object from a `GtkBuilder`.
unsafe fn get_obj<T>(b: &Refc<GtkBuilder>, name: ffi::NT) -> *mut T
{
gtk_builder_get_object(**b, name) as _
}
/// Connects a signal handler.
unsafe fn connect<T, U>(obj: *mut T, name: ffi::NT, cb: gpointer, d: *const U)
{
let cb = std::mem::transmute(cb);
g_signal_connect_data(obj as _, name, cb, d as _, None, 0);
}
/// Loads a `Pixbuf` from a resource.
unsafe fn load_img(path: ffi::NT) -> *mut GdkPixbuf
{
gdk_pixbuf_new_from_resource(path, ffi::null_mut())
}
/// Entry point.
fn main()
{
unsafe {
// get jacked, punk. opaque data structures are for nerds.
const RESOURCE_DATA: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/resources"));
// first we create the static resource header, which is really simple
let mut resource = GStaticResource{data: RESOURCE_DATA.as_ptr(),
data_len: RESOURCE_DATA.len(),
resource: ffi::null_mut(),
next: ffi::null_mut(),
padding: ffi::null_mut()};
// init it, now we can use it throughout the entire app without copying!
g_static_resource_init(&mut resource);
// create a container for the editor state
let edit = MapEditor::default();
let edit = RefCell::new(edit);
let edit = Rc::new(edit);
let eptr = Rc::into_raw(edit.clone());
// create and run the app
let app = Refc::new(gtk_application_new(APP_ID, 0));
connect(*app, ACTIVATE, app_activate as _, eptr);
g_application_run(*app as _, 0, ffi::null_mut());
// ok, clean up all this crap now
drop(app);
assert_eq!(Rc::strong_count(&edit), 1);
drop(edit);
// deinit the "static" data, and everything will be done
g_static_resource_fini(&mut resource);
}
}
impl<T> std::ops::Deref for Refc<'_, T>
{
type Target = *mut T;
fn deref(&self) -> &Self::Target {&self.p}
}
impl<T> Drop for Refc<'_, T>
{
fn drop(&mut self)
{
unsafe {
g_object_unref(self.p as _);
}
}
}
impl<T> Refc<'_, T>
{
fn new(p: *mut T) -> Self {Self{p, l: PhantomData}}
}
struct Refc<'a, T>
{
p: *mut T,
l: PhantomData<&'a *mut T>,
}
// EOF

View File

@ -1,83 +0,0 @@
use maraiah::{durandal::image::*, rozinante::draw::*};
fn flt_color(cr: impl Color) -> (f64, f64, f64)
{
fn flt_color(n: u16) -> f64 {f64::from(n) / f64::from(u16::max_value())}
(flt_color(cr.r()), flt_color(cr.g()), flt_color(cr.b()))
}
impl CacheImage for CairoPixbuf
{
fn w(&self) -> Coord {self.0.get_width() as Coord}
fn h(&self) -> Coord {self.0.get_height() as Coord}
}
impl CairoDrawArea
{
pub const fn new(ctx: cairo::Context, w: f64, h: f64) -> Self
{
CairoDrawArea{ctx, w: w as Coord, h: h as Coord}
}
}
impl DrawArea for CairoDrawArea
{
type NativeImage = CairoPixbuf;
fn w(&self) -> Coord {self.w}
fn h(&self) -> Coord {self.h}
fn clear(&self, cr: impl Color)
{
use cairo::{FontSlant, FontWeight};
self.rect(Rect{x: 0, y: 0, w: self.w(), h: self.h()}, cr);
self.ctx
.select_font_face("Sans", FontSlant::Normal, FontWeight::Normal);
self.ctx.set_font_size(14.0);
}
fn rect(&self, rect: Rect, cr: impl Color)
{
let x1 = f64::from(rect.x);
let y1 = f64::from(rect.y);
let x2 = f64::from(rect.w) + x1;
let y2 = f64::from(rect.h) + y1;
let (r, g, b) = flt_color(cr);
self.ctx.set_source_rgb(r, g, b);
self.ctx.rectangle(x1, y1, x2, y2);
self.ctx.fill();
}
fn text(&self, pos: Point, text: &str, cr: impl Color)
{
let (r, g, b) = flt_color(cr);
self.ctx.set_source_rgb(r, g, b);
self.ctx.move_to(f64::from(pos.0), f64::from(pos.1));
self.ctx.show_text(text);
}
fn image(&self, pos: Point, im: &Self::NativeImage)
{
use gdk::prelude::*;
self.ctx
.set_source_pixbuf(&im.0, f64::from(pos.0), f64::from(pos.1));
self.ctx.paint();
}
}
pub struct CairoPixbuf(pub gdk_pixbuf::Pixbuf);
pub struct CairoDrawArea
{
ctx: cairo::Context,
w: Coord,
h: Coord,
}
// EOF