mirror of
https://github.com/Alcaro/Flips.git
synced 2026-09-07 10:06:09 -05:00
Update Arlib
This commit is contained in:
157
arlib/Makefile
157
arlib/Makefile
@@ -1,8 +1,15 @@
|
||||
all: $(PROGRAM)_dummy
|
||||
#object filename structure:
|
||||
#obj/RULE___OBJNAME___PATH.o
|
||||
#RULE is the set of build flags for this file, nearly always DEFAULT
|
||||
#OBJNAME is the OBJNAME= make parameter, or a default value
|
||||
#PATH is the source filename, including extension, relative to project root, with slashes replaced with double underscore
|
||||
#example: obj/DEFAULT___linux___arlib__file-posix.cpp.o
|
||||
|
||||
SPACE :=
|
||||
SPACE +=
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
OS = windows
|
||||
#$(error objdump something and check which sections can be nuked)
|
||||
else
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
@@ -14,10 +21,10 @@ else
|
||||
endif
|
||||
endif
|
||||
|
||||
SPACE :=
|
||||
SPACE +=
|
||||
|
||||
ARGUI ?= 0
|
||||
AROPENGL ?= 0
|
||||
AROPENGL_D3DSYNC ?= 1
|
||||
ARIMAGE ?= 0
|
||||
ARTHREAD ?= 0
|
||||
ARSANDBOX ?= 0
|
||||
ARWUTF ?= 0
|
||||
@@ -33,8 +40,22 @@ CXX = g++
|
||||
CXXFLAGS = $(CFLAGS)
|
||||
LD = g++
|
||||
LFLAGS =
|
||||
OBJSUFFIX =
|
||||
CCXXFLAGS = -fvisibility=hidden -fno-exceptions -Wall -Wno-comment
|
||||
OBJNAME =
|
||||
CCXXFLAGS = -fvisibility=hidden -Wall
|
||||
ifneq ($(EXCEPTIONS),1)
|
||||
CCXXFLAGS += -fno-exceptions
|
||||
endif
|
||||
|
||||
#double gcc bug combo:
|
||||
#(1) GCC hates this pattern:
|
||||
#//define foo(a,b,c) \
|
||||
#// bar(a) \
|
||||
#// bar(b) \
|
||||
#// bar(c)
|
||||
# to my knowledge unreported
|
||||
#(2) '#pragma GCC diagnostic ignored "-Wcomment"' does nothing
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431
|
||||
CCXXFLAGS += -Wno-comment
|
||||
|
||||
EXESUFFIX =
|
||||
EXTRAOBJ ?=
|
||||
@@ -46,18 +67,18 @@ ifeq ($(OS),linux)
|
||||
CONF_CFLAGS += -pthread
|
||||
CONF_LFLAGS += -pthread
|
||||
endif
|
||||
OBJSUFFIX = -linux
|
||||
OBJNAME = linux
|
||||
TESTRUNNER = valgrind
|
||||
endif
|
||||
|
||||
ifeq ($(OS),windows)
|
||||
EXESUFFIX = .exe
|
||||
# EXTRAOBJ = obj/resource$(OBJSUFFIX).o
|
||||
# EXTRAOBJ = obj/resource$(OBJNAME).o
|
||||
# RC = windres
|
||||
# RCFLAGS =
|
||||
#obj/resource$(OBJSUFFIX).o: ico/*
|
||||
# $(RC) $(RCFLAGS) ico/minir.rc obj/resource$(OBJSUFFIX).o
|
||||
OBJSUFFIX = -windows
|
||||
#obj/resource$(OBJNAME).o: ico/*
|
||||
# $(RC) $(RCFLAGS) ico/minir.rc obj/resource$(OBJNAME).o
|
||||
OBJNAME = windows
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring test,$(MAKECMDGOALS)))
|
||||
@@ -68,7 +89,7 @@ ifneq (,$(findstring check,$(MAKECMDGOALS)))
|
||||
endif
|
||||
|
||||
OPTFLAGS := -Os -fomit-frame-pointer -fmerge-all-constants -fvisibility=hidden
|
||||
OPTFLAGS += -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables
|
||||
OPTFLAGS += -fno-unwind-tables -fno-asynchronous-unwind-tables
|
||||
OPTFLAGS += -ffunction-sections -fdata-sections
|
||||
OPTFLAGS += -Werror
|
||||
|
||||
@@ -76,7 +97,7 @@ ifeq ($(OPT),1)
|
||||
CFLAGS += $(OPTFLAGS)
|
||||
LFLAGS += -Wl,--gc-sections -s
|
||||
DEBUG = 0
|
||||
OBJSUFFIX += -opt
|
||||
OBJNAME += -opt
|
||||
endif
|
||||
ifeq ($(DEBUG),1)
|
||||
CFLAGS += -g -DDEBUG
|
||||
@@ -94,42 +115,58 @@ OUTNAME = $(PROGRAM)$(EXESUFFIX)
|
||||
|
||||
ifneq ($(SELFTEST),)
|
||||
CONF_CFLAGS += -DARLIB_TEST -Dmain=not_quite_main
|
||||
OBJSUFFIX += -test
|
||||
OBJNAME += -test
|
||||
endif
|
||||
|
||||
#stolen from http://stackoverflow.com/questions/22586084/makefile-with-multiple-rules-sharing-same-recipe-with-patternrules
|
||||
define ADDDIR_CORE
|
||||
$(eval OBJPREFIX := obj/_arlib_$(subst /,_,$(1))_)
|
||||
OBJS += $(patsubst $(1)/%.cpp,$(OBJPREFIX)%$(OBJSUFFIX).o,$(wildcard $(1)/*.cpp))
|
||||
$(OBJPREFIX)%$(OBJSUFFIX).o: $(1)/%.cpp | obj
|
||||
$$(CXX) $$(TRUE_CXXFLAGS) -c $$< -o $$@
|
||||
endef
|
||||
define ADDDIR
|
||||
$(eval $(call ADDDIR_CORE,$(1)))
|
||||
endef
|
||||
#OBJMANGLE(rule,sources) - takes C/C++ source files and returns the mangled name under the specified rule
|
||||
OBJMANGLE = $(patsubst %,obj/$1___$(OBJNAME)___%.o,$(subst /,__,$2))
|
||||
#SOURCENAME(obj) - takes a .o file, returned from OBJMANGLE, and returns the corresponding source file
|
||||
#does not handle multi-file inputs, use $(foreach)
|
||||
SOURCENAME = $(strip $(subst __,/,$(lastword $(subst ___,$(SPACE),$(patsubst obj/%.o,%,$1)))))
|
||||
#DOMAINNAME(obj) - takes a .o file, returned from OBJMANGLE, and returns the corresponding domain
|
||||
DOMAINNAME = $(firstword $(subst ___,$(SPACE),$(patsubst obj/%.o,%,$1)))
|
||||
|
||||
OBJSUFFIX := $(subst $(SPACE),,$(OBJSUFFIX))
|
||||
OBJS := $(patsubst %.cpp,obj/%$(OBJSUFFIX).o,$(wildcard *.cpp)) $(EXTRAOBJ)
|
||||
# obj/miniz$(OBJSUFFIX).o
|
||||
CFLAGS_DEFAULT =
|
||||
CFLAGS_NOWARN = -w
|
||||
|
||||
$(call ADDDIR,arlib)
|
||||
.SECONDEXPANSION:
|
||||
obj/%.c.o: $$(call SOURCENAME,$$@) | obj
|
||||
$(CC) $(TRUE_CFLAGS) $(CFLAGS_$(call DOMAINNAME,$@)) -c $< -o $@
|
||||
obj/%.cpp.o: $$(call SOURCENAME,$$@) | obj
|
||||
$(CXX) $(TRUE_CXXFLAGS) $(CFLAGS_$(call DOMAINNAME,$@)) -c $< -o $@
|
||||
|
||||
SOURCES += *.cpp arlib/*.cpp
|
||||
|
||||
ifeq ($(ARGUI),1)
|
||||
$(call ADDDIR,arlib/gui)
|
||||
SOURCES += arlib/gui/*.cpp
|
||||
ifeq ($(OS),windows)
|
||||
CONF_CFLAGS += -DARGUI_WINDOWS
|
||||
CONF_LFLAGS += -lgdi32 -lcomctl32 -lcomdlg32 -ldinput8 -ldxguid -lopengl32
|
||||
CONF_LFLAGS += -lgdi32 -lcomctl32 -lcomdlg32
|
||||
endif
|
||||
ifeq ($(OS),linux)
|
||||
CONF_CFLAGS += $(shell pkg-config --cflags gtk+-3.0) -DARGUI_GTK3 -DARGUIPROT_X11
|
||||
CONF_LFLAGS += -ldl -lX11 -lGL -lXi -lXext $(shell pkg-config --libs gtk+-3.0)
|
||||
CONF_LFLAGS += -ldl -lX11 $(shell pkg-config --libs gtk+-3.0)
|
||||
endif
|
||||
else
|
||||
CONF_CFLAGS += -DARGUI_NONE
|
||||
endif
|
||||
|
||||
ifeq ($(AROPENGL),1)
|
||||
ifeq ($(ARGUI),0)
|
||||
$(error can't use OpenGL without the GUI)
|
||||
endif
|
||||
SOURCES += arlib/opengl/*.cpp
|
||||
CONF_CFLAGS += -DARLIB_OPENGL
|
||||
ifeq ($(OS),linux)
|
||||
CONF_LFLAGS += -ldl
|
||||
endif
|
||||
ifeq ($(AROPENGL_D3DSYNC),1)
|
||||
CONF_CFLAGS += -DAROPENGL_D3DSYNC
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARTHREAD),1)
|
||||
$(call ADDDIR,arlib/thread)
|
||||
SOURCES += arlib/thread/*.cpp
|
||||
CONF_CFLAGS += -DARLIB_THREAD
|
||||
ifeq ($(OS),linux)
|
||||
CONF_CFLAGS += -pthread
|
||||
@@ -138,7 +175,7 @@ ifeq ($(ARTHREAD),1)
|
||||
endif
|
||||
|
||||
ifeq ($(ARSANDBOX),1)
|
||||
$(call ADDDIR,arlib/sandbox)
|
||||
SOURCES += arlib/sandbox/*.cpp
|
||||
CONF_CFLAGS += -DARLIB_SANDBOX
|
||||
#not true since the windows sandbox isn't a real sandbox
|
||||
#ifeq ($(OS),windows)
|
||||
@@ -150,12 +187,12 @@ ifeq ($(ARSANDBOX),1)
|
||||
endif
|
||||
|
||||
ifeq ($(ARWUTF),1)
|
||||
$(call ADDDIR,arlib/wutf)
|
||||
SOURCES += arlib/wutf/*.cpp
|
||||
CONF_CFLAGS += -DARLIB_WUTF
|
||||
endif
|
||||
|
||||
ifeq ($(ARSOCKET),1)
|
||||
$(call ADDDIR,arlib/socket)
|
||||
SOURCES += arlib/socket/*.cpp
|
||||
CONF_CFLAGS += -DARLIB_SOCKET
|
||||
|
||||
ifeq ($(OS),windows)
|
||||
@@ -168,37 +205,29 @@ ifeq ($(ARSOCKET),1)
|
||||
else ifeq ($(OS),windows)
|
||||
CONF_CFLAGS += -DARLIB_SSL_SCHANNEL
|
||||
CONF_LFLAGS += -lcrypt32 -lsecur32
|
||||
else ifeq ($(ARSOCKET_SSL),wolfssl)
|
||||
WOLFSSL_DIR = arlib/socket/wolfssl-3.9.0
|
||||
CONF_CFLAGS += -DARLIB_SSL_WOLFSSL -I$(WOLFSSL_DIR)
|
||||
OBJS += obj/_arlib_sp_wolfssl$(OBJSUFFIX).o
|
||||
#CFLAGS += $(OPTFLAGS)
|
||||
else ifeq ($(ARSOCKET_SSL),openssl)
|
||||
CONF_CFLAGS += -DARLIB_SSL_OPENSSL
|
||||
CONF_LFLAGS += -lssl -lcrypto
|
||||
else ifeq ($(ARSOCKET_SSL),gnutls)
|
||||
CONF_CFLAGS += -DARLIB_SSL_GNUTLS
|
||||
CONF_LFLAGS += -lgnutls
|
||||
else ifeq ($(ARSOCKET_SSL),tlse)
|
||||
CONF_CFLAGS += -DARLIB_SSL_TLSE
|
||||
OBJS += obj/_arlib_sp_tlse$(OBJSUFFIX).o
|
||||
SOURCES_NOWARN += arlib/deps/tlse.c
|
||||
else
|
||||
$(error unknown SSL library)
|
||||
endif
|
||||
endif
|
||||
|
||||
SOURCES += arlib/deps/miniz.c
|
||||
|
||||
TRUE_CFLAGS = -std=c99 $(CCXXFLAGS) $(CFLAGS) $(CONF_CFLAGS)
|
||||
TRUE_CXXFLAGS =-std=c++11 -fno-rtti $(CCXXFLAGS) $(CXXFLAGS) $(CONF_CXXFLAGS)
|
||||
TRUE_LFLAGS = $(LFLAGS) -fvisibility=hidden $(CONF_LFLAGS)
|
||||
|
||||
#double gcc bug combo:
|
||||
#(1) GCC hates this pattern:
|
||||
#//define foo(a,b,c) \
|
||||
#// bar(a) \
|
||||
#// bar(b) \
|
||||
#// bar(c)
|
||||
# to my knowledge unreported
|
||||
#(2) '#pragma GCC diagnostic ignored "-Wcomment"' does nothing
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431
|
||||
TRUE_CFLAGS += -Wno-comment
|
||||
TRUE_CXXFLAGS += -Wno-comment
|
||||
ifeq ($(OS),windows)
|
||||
TRUE_LFLAGS += -static-libgcc
|
||||
endif
|
||||
|
||||
#On Windows, cleaning up the object directory is expected to be done with 'del /q obj\*' in a batch script.
|
||||
clean:
|
||||
@@ -210,26 +239,18 @@ clean-prof:
|
||||
obj:
|
||||
mkdir obj
|
||||
|
||||
obj/miniz$(OBJSUFFIX).o: miniz.c | obj
|
||||
$(CC) $(TRUE_CFLAGS) -c $< -o $@
|
||||
|
||||
obj/_arlib_sp_wolfssl$(OBJSUFFIX).o: arlib/socket/wolfssl-lib.c | obj
|
||||
$(CC) -DARLIB_SSL_WOLFSSL_SP $(TRUE_CFLAGS) $(OPTFLAGS) -c $< -o $@
|
||||
DOMAINS += DEFAULT NOWARN
|
||||
|
||||
obj/_arlib_sp_tlse$(OBJSUFFIX).o: arlib/socket/tlse.c | obj
|
||||
$(CC) -DTLSE_IMPL -DTLS_AMALGAMATION -D_XOPEN_SOURCE=600 $(TRUE_CFLAGS) -w -c $< -o $@
|
||||
|
||||
obj/%-c$(OBJSUFFIX).o: %.c | obj
|
||||
$(CC) $(TRUE_CFLAGS) -c $< -o $@
|
||||
|
||||
obj/%$(OBJSUFFIX).o: %.cpp | obj
|
||||
$(CXX) $(TRUE_CXXFLAGS) -c $< -o $@
|
||||
OBJNAME := $(subst $(SPACE),,$(OBJNAME))
|
||||
SOURCES_DEFAULT := $(SOURCES)
|
||||
OBJS += $(foreach domain,$(sort $(DOMAINS)),$(call OBJMANGLE,$(domain),$(wildcard $(SOURCES_$(domain)))))
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
all: $(OUTNAME)
|
||||
$(OUTNAME): $(OBJS)
|
||||
$(LD) $+ $(TRUE_LFLAGS) -o $@ -lm
|
||||
|
||||
$(PROGRAM)_dummy: $(OUTNAME)
|
||||
|
||||
|
||||
|
||||
ifneq ($(SELFTEST),)
|
||||
@@ -240,5 +261,3 @@ test: obj/arlibtest$(EXESUFFIX)
|
||||
$(TESTRUNNER) obj/arlibtest$(EXESUFFIX)
|
||||
check: test
|
||||
endif
|
||||
|
||||
#todo: replace with ./test shell script (make test/check should still exist)
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
//TODO:
|
||||
//- cool down on string.h refcounting
|
||||
//- SSO for array<>
|
||||
//- make strings and arrays nullable, so failure vs empty answer can be determined
|
||||
//- window.h: remove pointers
|
||||
//- window.h: remove varargs
|
||||
//- msvc compat: add some define that, if absent, enables every feature
|
||||
|
||||
//WARNING: Arlib comes with zero stability guarantees. It can and will change in arbitrary ways, for any reason and at any time.
|
||||
|
||||
#pragma once
|
||||
#include "bml.h"
|
||||
#include "containers.h"
|
||||
@@ -9,22 +19,31 @@
|
||||
#include "serialize.h"
|
||||
#include "string.h"
|
||||
#include "stringconv.h"
|
||||
#include "test.h"
|
||||
#include "zip.h"
|
||||
|
||||
//not in #ifdef, it contains some dummy implementations if threads are disabled
|
||||
#include "thread/thread.h"
|
||||
|
||||
#if !defined(ARGUI_NONE) && !defined(ARGUI_WIN32) && !defined(ARGUI_GTK3)
|
||||
#if !defined(ARGUI_NONE) && !defined(ARGUI_WINDOWS) && !defined(ARGUI_GTK3)
|
||||
#define ARGUI_NONE
|
||||
#endif
|
||||
#ifndef ARGUI_NONE
|
||||
#include "gui/window.h"
|
||||
#endif
|
||||
|
||||
#ifdef ARLIB_OPENGL
|
||||
#include "opengl/aropengl.h"
|
||||
#endif
|
||||
|
||||
#ifdef ARLIB_WUTF
|
||||
#include "wutf.h"
|
||||
#include "wutf/wutf.h"
|
||||
#endif
|
||||
|
||||
#ifdef ARLIB_SANDBOX
|
||||
#include "sandbox.h"
|
||||
#include "sandbox/sandbox.h"
|
||||
#endif
|
||||
|
||||
#ifdef ARLIB_SOCKET
|
||||
#include "socket.h"
|
||||
#include "socket/socket.h"
|
||||
#endif
|
||||
|
||||
199
arlib/array.h
199
arlib/array.h
@@ -9,9 +9,7 @@
|
||||
//this object does not own its storage, it's just a pointer wrapper
|
||||
template<typename T> class arrayview {
|
||||
protected:
|
||||
class null_only;
|
||||
|
||||
T * items;
|
||||
T * items; // not const, despite not necessarily being writable; this makes arrayvieww/array a lot simpler
|
||||
size_t count;
|
||||
|
||||
//void clone(const arrayview<T>& other)
|
||||
@@ -33,7 +31,7 @@ public:
|
||||
this->count=0;
|
||||
}
|
||||
|
||||
arrayview(const null_only*)
|
||||
arrayview(null_t)
|
||||
{
|
||||
this->items=NULL;
|
||||
this->count=0;
|
||||
@@ -51,6 +49,29 @@ public:
|
||||
this->count = N;
|
||||
}
|
||||
|
||||
arrayview<T> slice(size_t first, size_t count) const { return arrayview<T>(this->items+first, count); }
|
||||
|
||||
T join() const
|
||||
{
|
||||
T out = this->items[0];
|
||||
for (size_t n=1;n<this->count;n++)
|
||||
{
|
||||
out += this->items[n];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T2> decltype(T() + T2()) join(T2 between) const
|
||||
{
|
||||
decltype(T() + T2()) out = this->items[0];
|
||||
for (size_t n=1;n < this->count;n++)
|
||||
{
|
||||
out += between;
|
||||
out += this->items[n];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
//arrayview(const arrayview<T>& other)
|
||||
//{
|
||||
// clone(other);
|
||||
@@ -61,19 +82,72 @@ public:
|
||||
// clone(other);
|
||||
// return *this;
|
||||
//}
|
||||
|
||||
const T* begin() { return this->items; }
|
||||
const T* end() { return this->items+this->count; }
|
||||
};
|
||||
|
||||
//size: two pointers
|
||||
//this one can write its storage, but doesn't own it
|
||||
template<typename T> class arrayvieww : public arrayview<T> {
|
||||
//T * items;
|
||||
//size_t count;
|
||||
public:
|
||||
|
||||
T& operator[](size_t n) { return this->items[n]; }
|
||||
const T& operator[](size_t n) const { return this->items[n]; }
|
||||
|
||||
T* ptr() { return this->items; }
|
||||
const T* ptr() const { return this->items; }
|
||||
|
||||
arrayvieww()
|
||||
{
|
||||
this->items=NULL;
|
||||
this->count=0;
|
||||
}
|
||||
|
||||
arrayvieww(null_t)
|
||||
{
|
||||
this->items=NULL;
|
||||
this->count=0;
|
||||
}
|
||||
|
||||
arrayvieww(T * ptr, size_t count)
|
||||
{
|
||||
this->items = ptr;
|
||||
this->count = count;
|
||||
}
|
||||
|
||||
arrayvieww(const arrayvieww<T>& other)
|
||||
{
|
||||
this->items = other.items;
|
||||
this->count = other.count;
|
||||
}
|
||||
|
||||
arrayvieww<T> operator=(arrayvieww<T> other)
|
||||
{
|
||||
this->items = other.items;
|
||||
this->count = other.count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
arrayvieww<T> slice(size_t first, size_t count) { return arrayvieww<T>(this->items+first, count); }
|
||||
|
||||
T* begin() { return this->items; }
|
||||
T* end() { return this->items+this->count; }
|
||||
};
|
||||
|
||||
//size: two pointers, plus one T per item
|
||||
//this one owns its storage
|
||||
template<typename T> class array : public arrayview<T> {
|
||||
//this one owns its storage, and manages its memory
|
||||
template<typename T> class array : public arrayvieww<T> {
|
||||
//T * items;
|
||||
//size_t count;
|
||||
|
||||
void clone(const array<T>& other)
|
||||
void clone(const arrayview<T>& other)
|
||||
{
|
||||
this->count=other.count;
|
||||
this->items=malloc(sizeof(T)*bitround(this->count));
|
||||
for (size_t i=0;i<this->count;i++) new(&this->items[i]) T(other.items[i]);
|
||||
this->count = other.size(); // I can somehow not access non-this instances of my base class, so let's just use the public interface.
|
||||
this->items = malloc(sizeof(T)*bitround(this->count));
|
||||
for (size_t i=0;i<this->count;i++) new(&this->items[i]) T(other.ptr()[i]);
|
||||
}
|
||||
|
||||
void swap(array<T>& other)
|
||||
@@ -86,22 +160,28 @@ template<typename T> class array : public arrayview<T> {
|
||||
this->count = newcount;
|
||||
}
|
||||
|
||||
void resize_grow(size_t count)
|
||||
void resize_grow_noinit(size_t count)
|
||||
{
|
||||
if (this->count >= count) return;
|
||||
size_t bufsize_pre=bitround(this->count);
|
||||
size_t bufsize_post=bitround(count);
|
||||
size_t bufsize_pre = bitround(this->count);
|
||||
size_t bufsize_post = bitround(count);
|
||||
if (bufsize_pre != bufsize_post) this->items=realloc(this->items, sizeof(T)*bufsize_post);
|
||||
for (size_t i=this->count;i<count;i++)
|
||||
this->count=count;
|
||||
}
|
||||
|
||||
void resize_grow(size_t count)
|
||||
{
|
||||
size_t prevcount = this->count;
|
||||
resize_grow_noinit(count);
|
||||
for (size_t i=prevcount;i<count;i++)
|
||||
{
|
||||
new(&this->items[i]) T();
|
||||
}
|
||||
this->count=count;
|
||||
}
|
||||
|
||||
void resize_shrink(size_t count)
|
||||
{
|
||||
if (this->count < count) return;
|
||||
if (this->count <= count) return;
|
||||
for (size_t i=count;i<this->count;i++)
|
||||
{
|
||||
this->items[i].~T();
|
||||
@@ -120,47 +200,18 @@ template<typename T> class array : public arrayview<T> {
|
||||
|
||||
public:
|
||||
T& operator[](size_t n) { resize_grow(n+1); return this->items[n]; }
|
||||
const T& operator[](size_t n) const { return this->items[n]; }
|
||||
|
||||
T* ptr() { return this->items; }
|
||||
void resize(size_t len) { resize_to(len); }
|
||||
|
||||
T join() const
|
||||
{
|
||||
T out = this->items[0];
|
||||
for (size_t n=1;n<this->count;n++)
|
||||
{
|
||||
out += this->items[n];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
T join(T between) const
|
||||
{
|
||||
T out = this->items[0];
|
||||
for (size_t n=1;n < this->count;n++)
|
||||
{
|
||||
out += between;
|
||||
out += this->items[n];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
T join(char between) const
|
||||
{
|
||||
T out = this->items[0];
|
||||
for (size_t n=1;n<this->count;n++)
|
||||
{
|
||||
out += between;
|
||||
out += this->items[n];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void append(const T& item) { size_t pos = this->count; resize_grow(pos+1); this->items[pos] = item; }
|
||||
void reset() { resize_shrink(0); }
|
||||
|
||||
arrayview<T> slice(size_t first, size_t count) { return arrayview<T>(this->items+first, this->count); }
|
||||
void remove(size_t index)
|
||||
{
|
||||
this->items[index].~T();
|
||||
memmove(this->items+index, this->items+index+1, sizeof(T)*(this->count-1-index));
|
||||
this->count--;
|
||||
}
|
||||
|
||||
array()
|
||||
{
|
||||
@@ -168,17 +219,26 @@ public:
|
||||
this->count=0;
|
||||
}
|
||||
|
||||
array(null_t)
|
||||
{
|
||||
this->items=NULL;
|
||||
this->count=0;
|
||||
}
|
||||
|
||||
array(const array<T>& other)
|
||||
{
|
||||
clone(other);
|
||||
}
|
||||
|
||||
#ifdef HAVE_MOVE
|
||||
array(const arrayview<T>& other)
|
||||
{
|
||||
clone(other);
|
||||
}
|
||||
|
||||
array(array<T>&& other)
|
||||
{
|
||||
swap(other);
|
||||
}
|
||||
#endif
|
||||
|
||||
array<T> operator=(array<T> other)
|
||||
{
|
||||
@@ -186,19 +246,36 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
static array<T> create_from(T* ptr, size_t count)
|
||||
{
|
||||
array<T> ret;
|
||||
ret.items = ptr;
|
||||
ret.count = count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
~array()
|
||||
{
|
||||
for (size_t i=0;i<this->count;i++) this->items[i].~T();
|
||||
free(this->items);
|
||||
}
|
||||
|
||||
//takes ownership of the given data
|
||||
static array<T> create_usurp(T* ptr, size_t count)
|
||||
{
|
||||
array<T> ret;
|
||||
ret.items = ptr;
|
||||
ret.count = 0;
|
||||
ret.resize_grow_noinit(count);
|
||||
return ret;
|
||||
}
|
||||
|
||||
array<T>& operator+=(arrayview<T> other)
|
||||
{
|
||||
size_t prevcount = this->count;
|
||||
size_t othercount = other.size(); // in case this==other
|
||||
|
||||
resize_grow_noinit(prevcount + othercount);
|
||||
|
||||
for (size_t i=0;i<othercount;i++)
|
||||
{
|
||||
new(&this->items[prevcount + i]) T(other[i]);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
12
arlib/bml.h
12
arlib/bml.h
@@ -2,7 +2,6 @@
|
||||
#include "global.h"
|
||||
#include "array.h"
|
||||
#include "string.h"
|
||||
#include "serialize.h"
|
||||
|
||||
//This is a streaming parser. For each node, { enter } then { exit } is returned; more enter/exit pairs may be present between them.
|
||||
//For example, the document
|
||||
@@ -10,7 +9,7 @@
|
||||
parent child=1
|
||||
parent2
|
||||
*/
|
||||
//would yield { enter, parent, "" } { enter, child, 1 } { exit } { exit } { enter, parent2, "" } { exit }.
|
||||
//would yield { enter, "parent", "" } { enter, "child", "1" } { exit } { exit } { enter, "parent2", "" } { exit }.
|
||||
//The parser keeps trying after an { error }, giving you a partial view of the damaged document; however,
|
||||
// there are no guarantees on how much you can see, and it is likely for one error to cause many more, or misplaced nodes.
|
||||
//enter/exit is always paired, even in the presense of errors.
|
||||
@@ -23,6 +22,11 @@ public:
|
||||
cstring name;
|
||||
cstring value; // or error message
|
||||
// putting error first would be cleaner in the parser, but reader clarity is more important, and this name is better
|
||||
|
||||
//these constructors are because MSVC2013 can't parse (event){ enter, "foo", "bar" }
|
||||
event(int action) : action(action) {}
|
||||
event(int action, cstring name) : action(action), name(name) {}
|
||||
event(int action, cstring name, cstring value) : action(action), name(name), value(value) {}
|
||||
};
|
||||
|
||||
//Remember the cstring rules: If this cstring doesn't hold a reference, don't touch its buffer until the object is disposed.
|
||||
@@ -68,9 +72,9 @@ public:
|
||||
multiline // node\n :value
|
||||
};
|
||||
|
||||
//If you pass in data that's not valid for that mode (for example, val="foo bar" and mode=eq won't work),
|
||||
//If you pass in data that's not valid for that mode (for example, val="foo bar" and mode=eq),
|
||||
// then it silently switches to the lowest working mode (in the above example, quote).
|
||||
//Since enter() implies the tag has children, it will also disobey the inline modes; use node() for that.
|
||||
//Since enter() implies the tag has children, it will also disobey the inline modes; use node() if you want it inlined.
|
||||
void enter(cstring name, cstring val, mode m = anon);
|
||||
void exit();
|
||||
void linebreak();
|
||||
|
||||
@@ -225,10 +225,10 @@ static bool isendl(char ch)
|
||||
|
||||
static cstring cutline(cstring& input)
|
||||
{
|
||||
//pointers are generally bad ideas, but this is such a hotspot it's worth it
|
||||
const char * inputraw = input.nt();
|
||||
//pointers are generally a bad idea, but this is such a hotspot it's worth it
|
||||
const uint8_t * inputraw = input.bytes().ptr();
|
||||
size_t nlpos = 0;
|
||||
if (input.hasnt())
|
||||
if (input.bytes_hasterm())
|
||||
{
|
||||
while (!isendl(inputraw[nlpos])) nlpos++;
|
||||
}
|
||||
@@ -263,7 +263,7 @@ inline bool bmlparser::getline()
|
||||
while (m_thisline[indentlen] == ' ' || m_thisline[indentlen] == '\t') indentlen++;
|
||||
|
||||
int sharedindent = min(indentlen, m_indent.length());
|
||||
bool badwhite = (memcmp(m_thisline.nt(), m_indent.nt(), sharedindent)!=0);
|
||||
bool badwhite = (memcmp(m_thisline.bytes().ptr(), m_indent.bytes().ptr(), sharedindent)!=0);
|
||||
|
||||
m_indent = cut(m_thisline, 0, indentlen, 0);
|
||||
|
||||
@@ -275,7 +275,7 @@ bmlparser::event bmlparser::next()
|
||||
if (m_exit)
|
||||
{
|
||||
m_exit = false;
|
||||
return (event){ exit };
|
||||
return event(exit);
|
||||
}
|
||||
|
||||
if (m_inlines)
|
||||
@@ -295,7 +295,7 @@ bmlparser::event bmlparser::next()
|
||||
|
||||
if (!m_thisline && m_data)
|
||||
{
|
||||
if (!getline()) return (event){ error, "", "Mixed tabs and spaces" };
|
||||
if (!getline()) return event(error, "", "Mixed tabs and spaces");
|
||||
}
|
||||
|
||||
if (m_indent_step.size() > m_indent.length())
|
||||
@@ -307,20 +307,20 @@ bmlparser::event bmlparser::next()
|
||||
// but only if the document contains mix-tab-space already.
|
||||
if (m_indent_step.size() > m_indent.length()) m_indent += m_indent[0];
|
||||
else m_indent = m_indent.csubstr(0, ~1);
|
||||
return (event){ error, "", "Invalid indentation depth" };
|
||||
return event(error, "", "Invalid indentation depth");
|
||||
}
|
||||
|
||||
int lasttrue = m_indent_step.size()-2; // -1 for [size()] being OOB, -1 to skip the true at [size()-1] and discard it
|
||||
while (lasttrue>=0 && m_indent_step[lasttrue]==false) lasttrue--;
|
||||
|
||||
m_indent_step.resize(lasttrue+1);
|
||||
return (event){ exit };
|
||||
return event(exit);
|
||||
}
|
||||
|
||||
if (!m_thisline)
|
||||
{
|
||||
if (m_indent_step.size()) goto handle_indent;
|
||||
return (event){ finish };
|
||||
return event(finish);
|
||||
}
|
||||
|
||||
m_inlines = m_thisline;
|
||||
@@ -331,36 +331,36 @@ bmlparser::event bmlparser::next()
|
||||
cstring value;
|
||||
if (!bml_parse_inline_node(m_inlines, node, hasvalue, value))
|
||||
{
|
||||
return (event){ error, "", value };
|
||||
return event(error, "", value);
|
||||
}
|
||||
|
||||
int indentlen = m_indent.length(); // changed by getline
|
||||
//multilines
|
||||
if (!hasvalue)
|
||||
{
|
||||
if (!getline()) return (event){ error, "", "Mixed tabs and spaces" };
|
||||
if (!getline()) return event(error, "", "Mixed tabs and spaces");
|
||||
if (m_thisline[0] == ':')
|
||||
{
|
||||
size_t inner_indent = m_indent.length();
|
||||
value = m_thisline.csubstr(1, ~0);
|
||||
if (!getline()) return (event){ error, "", "Mixed tabs and spaces" };
|
||||
if (!getline()) return event(error, "", "Mixed tabs and spaces");
|
||||
while (m_thisline[0] == ':')
|
||||
{
|
||||
if (inner_indent != m_indent.length()) return (event){ error, "", "Multi-line values must have constant indentation" };
|
||||
if (inner_indent != m_indent.length()) return event(error, "", "Multi-line values must have constant indentation");
|
||||
value += "\n" + m_thisline.csubstr(1, ~0);
|
||||
if (!getline()) return (event){ error, "", "Mixed tabs and spaces" };
|
||||
if (!getline()) return event(error, "", "Mixed tabs and spaces");
|
||||
}
|
||||
|
||||
if (m_indent.length() != inner_indent)
|
||||
{
|
||||
if (m_indent.length() > inner_indent) return (event){ error, "", "Can't change indentation after a multi-line value" };
|
||||
if (!m_indent_step[m_indent.length()]) return (event){ error, "", "Invalid indentation depth" };
|
||||
if (m_indent.length() > inner_indent) return event(error, "", "Can't change indentation after a multi-line value");
|
||||
if (!m_indent_step[m_indent.length()]) return event(error, "", "Invalid indentation depth");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_indent_step[indentlen] = true;
|
||||
return (event){ enter, node, value };
|
||||
return event(enter, node, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -502,6 +502,32 @@ bmlparser::event test4e[]={
|
||||
{ e_finish }
|
||||
};
|
||||
|
||||
//screwy whitespace on otherwise blank lines is probably allowed
|
||||
//I can't justify allowing blank lines only,
|
||||
//I can't justify allowing only one of same-as-above and same-as-below,
|
||||
//and I can't justify allowing three different options but not all of them.
|
||||
//therefore, only one option remains.
|
||||
const char * test5 =
|
||||
"a\n"
|
||||
" b\n"
|
||||
"\n"
|
||||
" \n"
|
||||
" \n"
|
||||
" \n"
|
||||
" \n"
|
||||
" \n"
|
||||
"\t\n"
|
||||
" c\n";
|
||||
bmlparser::event test5e[]={
|
||||
{ e_enter, "a" },
|
||||
{ e_enter, "b" },
|
||||
{ e_enter, "c" },
|
||||
{ e_exit },
|
||||
{ e_exit },
|
||||
{ e_exit },
|
||||
{ e_finish }
|
||||
};
|
||||
|
||||
static void testbml(const char * bml, bmlparser::event* expected)
|
||||
{
|
||||
bmlparser parser(bml);
|
||||
@@ -530,9 +556,9 @@ static void testbml_error(const char * bml)
|
||||
while (true)
|
||||
{
|
||||
bmlparser::event ev = parser.next();
|
||||
if (events==999)
|
||||
printf("a=%i [%s] [%s]\n\n", ev.action, ev.name.data(), ev.value.data());
|
||||
if (ev.action == e_error) error = true; // any error is fine, really
|
||||
//if (events==999)
|
||||
//printf("a=%i [%s] [%s]\n\n", ev.action, ev.name.data().ptr(), ev.value.data().ptr());
|
||||
if (ev.action == e_error) error = true; // any error is fine
|
||||
if (ev.action == e_enter) depth++;
|
||||
if (ev.action == e_exit) depth--;
|
||||
if (ev.action == e_finish) break;
|
||||
@@ -551,6 +577,7 @@ test()
|
||||
testcall(testbml(test2, test2e));
|
||||
testcall(testbml(test3, test3e));
|
||||
testcall(testbml(test4, test4e));
|
||||
testcall(testbml(test5, test5e));
|
||||
|
||||
testcall(testbml_error("*")); // invalid node name
|
||||
testcall(testbml_error("a=\"")); // unclosed quote
|
||||
@@ -567,5 +594,7 @@ test()
|
||||
testcall(testbml_error("a\n :b\n c"));
|
||||
testcall(testbml_error("a\n :b\n\t:c"));
|
||||
testcall(testbml_error("a\n :b\n\tc"));
|
||||
testcall(testbml_error("a\n :b\n\n :c"));//blank line in multiline
|
||||
testcall(testbml_error("a\n :b\n \n :c"));//this too
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
inline string bmlwriter::indent()
|
||||
{
|
||||
string ret;
|
||||
char* ptr = ret.construct(m_indent*2);
|
||||
memset(ptr, ' ', m_indent*2);
|
||||
arrayvieww<byte> bytes = ret.construct(m_indent*2);
|
||||
memset(bytes.ptr(), ' ', m_indent*2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,3 +7,5 @@
|
||||
//#include "fifo.h"
|
||||
//#include "hashmap.h"
|
||||
//#include "multiint.h"
|
||||
#include "maybe.h"
|
||||
#include "tuple.h"
|
||||
|
||||
@@ -4,18 +4,20 @@ static const uint32_t crctable_4bits[]={
|
||||
0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC, 0x76DC4190, 0x6B6B51F4, 0x4DB26158, 0x5005713C,
|
||||
0xEDB88320, 0xF00F9344, 0xD6D6A3E8, 0xCB61B38C, 0x9B64C2B0, 0x86D3D2D4, 0xA00AE278, 0xBDBDF21C,
|
||||
};
|
||||
uint32_t crc32_update(const uint8_t* data, size_t len, uint32_t crc)
|
||||
uint32_t crc32_update(arrayview<uint8_t> data, uint32_t crc)
|
||||
{
|
||||
const uint8_t* ptr = data.ptr();
|
||||
size_t len = data.size();
|
||||
crc = ~crc;
|
||||
for (size_t i=0;i<len;i++)
|
||||
{
|
||||
crc = crctable_4bits[(crc^ data[i] )&0x0F] ^ (crc>>4);
|
||||
crc = crctable_4bits[(crc^(data[i]>>4))&0x0F] ^ (crc>>4);
|
||||
crc = crctable_4bits[(crc^ ptr[i] )&0x0F] ^ (crc>>4);
|
||||
crc = crctable_4bits[(crc^(ptr[i]>>4))&0x0F] ^ (crc>>4);
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
uint32_t crc32(const uint8_t* data, size_t len)
|
||||
uint32_t crc32(arrayview<uint8_t> data)
|
||||
{
|
||||
return crc32_update(data, len, 0);
|
||||
return crc32_update(data, 0);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "global.h"
|
||||
#include "array.h"
|
||||
|
||||
uint32_t crc32(const uint8_t* data, size_t len);
|
||||
uint32_t crc32_update(const uint8_t* data, size_t len, uint32_t crc);
|
||||
//uses 0xEDB88320 as generator polynomial
|
||||
uint32_t crc32(arrayview<uint8_t> data);
|
||||
uint32_t crc32_update(arrayview<uint8_t> data, uint32_t crc);
|
||||
|
||||
1044
arlib/deps/gl.h
Normal file
1044
arlib/deps/gl.h
Normal file
File diff suppressed because it is too large
Load Diff
11007
arlib/deps/glext.h
Normal file
11007
arlib/deps/glext.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,5 @@
|
||||
// from https://github.com/eduardsui/tlse
|
||||
// slightly modified
|
||||
|
||||
#ifdef TLSE_IMPL
|
||||
#define CRYPT 0x0117
|
||||
#define LTC_NO_ROLC
|
||||
|
||||
@@ -34372,4 +34370,3 @@ int md5_test(void)
|
||||
/* $Source$ */
|
||||
/* $Revision$ */
|
||||
/* $Date$ */
|
||||
#endif
|
||||
4916
arlib/deps/miniz.c
Normal file
4916
arlib/deps/miniz.c
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,3 @@
|
||||
// from https://github.com/eduardsui/tlse
|
||||
#ifndef TLSE_H
|
||||
#define TLSE_H
|
||||
|
||||
@@ -9,16 +8,21 @@
|
||||
#define TLS_LEGACY_SUPPORT
|
||||
// SSL_* style blocking APIs
|
||||
#define SSL_COMPATIBLE_INTERFACE
|
||||
// support ChaCha20/Poly1305
|
||||
#define TLS_WITH_CHACHA20_POLY1305
|
||||
// support forward secrecy (Diffie-Hellman ephemeral)
|
||||
#define TLS_FORWARD_SECRECY
|
||||
// support client-side ECDHE
|
||||
#define TLS_CLIENT_ECDHE
|
||||
// suport ecdsa
|
||||
#define TLS_ECDSA_SUPPORTED
|
||||
// suport ecdsa client-side
|
||||
// #define TLS_CLIENT_ECDSA
|
||||
// TLS renegotiation is disabled by default (secured or not)
|
||||
// do not uncomment next line!
|
||||
// #define TLS_ACCEPT_SECURE_RENEGOTIATION
|
||||
|
||||
#define SSL_V30 0x0300
|
||||
#define TLS_V10 0x0301
|
||||
#define TLS_V11 0x0302
|
||||
#define TLS_V12 0x0303
|
||||
@@ -224,6 +228,9 @@ int tls_sni_set(struct TLSContext *context, const char *sni);
|
||||
int tls_load_root_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size);
|
||||
int tls_default_verify(struct TLSContext *context, struct TLSCertificate **certificate_chain, int len);
|
||||
void tls_print_certificate(const char *fname);
|
||||
int tls_add_alpn(struct TLSContext *context, const char *alpn);
|
||||
int tls_alpn_contains(struct TLSContext *context, const char *alpn, unsigned char alpn_size);
|
||||
const char *tls_alpn(struct TLSContext *context);
|
||||
|
||||
#ifdef SSL_COMPATIBLE_INTERFACE
|
||||
#define SSL_SERVER_RSA_CERT 1
|
||||
@@ -240,6 +247,8 @@ void tls_print_certificate(const char *fname);
|
||||
typedef struct {
|
||||
int fd;
|
||||
tls_validation_function certificate_verify;
|
||||
void *recv;
|
||||
void *send;
|
||||
void *user_data;
|
||||
} SSLUserData;
|
||||
|
||||
@@ -271,6 +280,7 @@ void tls_print_certificate(const char *fname);
|
||||
int SSL_write(struct TLSContext *context, void *buf, unsigned int len);
|
||||
int SSL_read(struct TLSContext *context, void *buf, unsigned int len);
|
||||
int SSL_pending(struct TLSContext *context);
|
||||
int SSL_set_io(struct TLSContext *context, void *recv, void *send);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
825
arlib/deps/wglext.h
Normal file
825
arlib/deps/wglext.h
Normal file
@@ -0,0 +1,825 @@
|
||||
#ifndef __wglext_h_
|
||||
#define __wglext_h_ 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Copyright (c) 2013 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||
** copy of this software and/or associated documentation files (the
|
||||
** "Materials"), to deal in the Materials without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
** permit persons to whom the Materials are furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included
|
||||
** in all copies or substantial portions of the Materials.
|
||||
**
|
||||
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*/
|
||||
/*
|
||||
** This header is generated from the Khronos OpenGL / OpenGL ES XML
|
||||
** API Registry. The current version of the Registry, generator scripts
|
||||
** used to make the header, and the header can be found at
|
||||
** http://www.opengl.org/registry/
|
||||
**
|
||||
** Khronos $Revision$ on $Date$
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#define WGL_WGLEXT_VERSION 20130710
|
||||
|
||||
/* Generated C header for:
|
||||
* API: wgl
|
||||
* Versions considered: .*
|
||||
* Versions emitted: _nomatch_^
|
||||
* Default extensions included: wgl
|
||||
* Additional extensions included: _nomatch_^
|
||||
* Extensions removed: _nomatch_^
|
||||
*/
|
||||
|
||||
#ifndef WGL_ARB_buffer_region
|
||||
#define WGL_ARB_buffer_region 1
|
||||
#define WGL_FRONT_COLOR_BUFFER_BIT_ARB 0x00000001
|
||||
#define WGL_BACK_COLOR_BUFFER_BIT_ARB 0x00000002
|
||||
#define WGL_DEPTH_BUFFER_BIT_ARB 0x00000004
|
||||
#define WGL_STENCIL_BUFFER_BIT_ARB 0x00000008
|
||||
typedef HANDLE (WINAPI * PFNWGLCREATEBUFFERREGIONARBPROC) (HDC hDC, int iLayerPlane, UINT uType);
|
||||
typedef VOID (WINAPI * PFNWGLDELETEBUFFERREGIONARBPROC) (HANDLE hRegion);
|
||||
typedef BOOL (WINAPI * PFNWGLSAVEBUFFERREGIONARBPROC) (HANDLE hRegion, int x, int y, int width, int height);
|
||||
typedef BOOL (WINAPI * PFNWGLRESTOREBUFFERREGIONARBPROC) (HANDLE hRegion, int x, int y, int width, int height, int xSrc, int ySrc);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
HANDLE WINAPI wglCreateBufferRegionARB (HDC hDC, int iLayerPlane, UINT uType);
|
||||
VOID WINAPI wglDeleteBufferRegionARB (HANDLE hRegion);
|
||||
BOOL WINAPI wglSaveBufferRegionARB (HANDLE hRegion, int x, int y, int width, int height);
|
||||
BOOL WINAPI wglRestoreBufferRegionARB (HANDLE hRegion, int x, int y, int width, int height, int xSrc, int ySrc);
|
||||
#endif
|
||||
#endif /* WGL_ARB_buffer_region */
|
||||
|
||||
#ifndef WGL_ARB_create_context
|
||||
#define WGL_ARB_create_context 1
|
||||
#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001
|
||||
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
|
||||
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
|
||||
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
|
||||
#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
|
||||
#define WGL_CONTEXT_FLAGS_ARB 0x2094
|
||||
#define ERROR_INVALID_VERSION_ARB 0x2095
|
||||
typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShareContext, const int *attribList);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
HGLRC WINAPI wglCreateContextAttribsARB (HDC hDC, HGLRC hShareContext, const int *attribList);
|
||||
#endif
|
||||
#endif /* WGL_ARB_create_context */
|
||||
|
||||
#ifndef WGL_ARB_create_context_profile
|
||||
#define WGL_ARB_create_context_profile 1
|
||||
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
|
||||
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
|
||||
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
|
||||
#define ERROR_INVALID_PROFILE_ARB 0x2096
|
||||
#endif /* WGL_ARB_create_context_profile */
|
||||
|
||||
#ifndef WGL_ARB_create_context_robustness
|
||||
#define WGL_ARB_create_context_robustness 1
|
||||
#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004
|
||||
#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252
|
||||
#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
|
||||
#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261
|
||||
#endif /* WGL_ARB_create_context_robustness */
|
||||
|
||||
#ifndef WGL_ARB_extensions_string
|
||||
#define WGL_ARB_extensions_string 1
|
||||
typedef const char *(WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC) (HDC hdc);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
const char *WINAPI wglGetExtensionsStringARB (HDC hdc);
|
||||
#endif
|
||||
#endif /* WGL_ARB_extensions_string */
|
||||
|
||||
#ifndef WGL_ARB_framebuffer_sRGB
|
||||
#define WGL_ARB_framebuffer_sRGB 1
|
||||
#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9
|
||||
#endif /* WGL_ARB_framebuffer_sRGB */
|
||||
|
||||
#ifndef WGL_ARB_make_current_read
|
||||
#define WGL_ARB_make_current_read 1
|
||||
#define ERROR_INVALID_PIXEL_TYPE_ARB 0x2043
|
||||
#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054
|
||||
typedef BOOL (WINAPI * PFNWGLMAKECONTEXTCURRENTARBPROC) (HDC hDrawDC, HDC hReadDC, HGLRC hglrc);
|
||||
typedef HDC (WINAPI * PFNWGLGETCURRENTREADDCARBPROC) (void);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglMakeContextCurrentARB (HDC hDrawDC, HDC hReadDC, HGLRC hglrc);
|
||||
HDC WINAPI wglGetCurrentReadDCARB (void);
|
||||
#endif
|
||||
#endif /* WGL_ARB_make_current_read */
|
||||
|
||||
#ifndef WGL_ARB_multisample
|
||||
#define WGL_ARB_multisample 1
|
||||
#define WGL_SAMPLE_BUFFERS_ARB 0x2041
|
||||
#define WGL_SAMPLES_ARB 0x2042
|
||||
#endif /* WGL_ARB_multisample */
|
||||
|
||||
#ifndef WGL_ARB_pbuffer
|
||||
#define WGL_ARB_pbuffer 1
|
||||
DECLARE_HANDLE(HPBUFFERARB);
|
||||
#define WGL_DRAW_TO_PBUFFER_ARB 0x202D
|
||||
#define WGL_MAX_PBUFFER_PIXELS_ARB 0x202E
|
||||
#define WGL_MAX_PBUFFER_WIDTH_ARB 0x202F
|
||||
#define WGL_MAX_PBUFFER_HEIGHT_ARB 0x2030
|
||||
#define WGL_PBUFFER_LARGEST_ARB 0x2033
|
||||
#define WGL_PBUFFER_WIDTH_ARB 0x2034
|
||||
#define WGL_PBUFFER_HEIGHT_ARB 0x2035
|
||||
#define WGL_PBUFFER_LOST_ARB 0x2036
|
||||
typedef HPBUFFERARB (WINAPI * PFNWGLCREATEPBUFFERARBPROC) (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList);
|
||||
typedef HDC (WINAPI * PFNWGLGETPBUFFERDCARBPROC) (HPBUFFERARB hPbuffer);
|
||||
typedef int (WINAPI * PFNWGLRELEASEPBUFFERDCARBPROC) (HPBUFFERARB hPbuffer, HDC hDC);
|
||||
typedef BOOL (WINAPI * PFNWGLDESTROYPBUFFERARBPROC) (HPBUFFERARB hPbuffer);
|
||||
typedef BOOL (WINAPI * PFNWGLQUERYPBUFFERARBPROC) (HPBUFFERARB hPbuffer, int iAttribute, int *piValue);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
HPBUFFERARB WINAPI wglCreatePbufferARB (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList);
|
||||
HDC WINAPI wglGetPbufferDCARB (HPBUFFERARB hPbuffer);
|
||||
int WINAPI wglReleasePbufferDCARB (HPBUFFERARB hPbuffer, HDC hDC);
|
||||
BOOL WINAPI wglDestroyPbufferARB (HPBUFFERARB hPbuffer);
|
||||
BOOL WINAPI wglQueryPbufferARB (HPBUFFERARB hPbuffer, int iAttribute, int *piValue);
|
||||
#endif
|
||||
#endif /* WGL_ARB_pbuffer */
|
||||
|
||||
#ifndef WGL_ARB_pixel_format
|
||||
#define WGL_ARB_pixel_format 1
|
||||
#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
|
||||
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
|
||||
#define WGL_DRAW_TO_BITMAP_ARB 0x2002
|
||||
#define WGL_ACCELERATION_ARB 0x2003
|
||||
#define WGL_NEED_PALETTE_ARB 0x2004
|
||||
#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005
|
||||
#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006
|
||||
#define WGL_SWAP_METHOD_ARB 0x2007
|
||||
#define WGL_NUMBER_OVERLAYS_ARB 0x2008
|
||||
#define WGL_NUMBER_UNDERLAYS_ARB 0x2009
|
||||
#define WGL_TRANSPARENT_ARB 0x200A
|
||||
#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037
|
||||
#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
|
||||
#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
|
||||
#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
|
||||
#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
|
||||
#define WGL_SHARE_DEPTH_ARB 0x200C
|
||||
#define WGL_SHARE_STENCIL_ARB 0x200D
|
||||
#define WGL_SHARE_ACCUM_ARB 0x200E
|
||||
#define WGL_SUPPORT_GDI_ARB 0x200F
|
||||
#define WGL_SUPPORT_OPENGL_ARB 0x2010
|
||||
#define WGL_DOUBLE_BUFFER_ARB 0x2011
|
||||
#define WGL_STEREO_ARB 0x2012
|
||||
#define WGL_PIXEL_TYPE_ARB 0x2013
|
||||
#define WGL_COLOR_BITS_ARB 0x2014
|
||||
#define WGL_RED_BITS_ARB 0x2015
|
||||
#define WGL_RED_SHIFT_ARB 0x2016
|
||||
#define WGL_GREEN_BITS_ARB 0x2017
|
||||
#define WGL_GREEN_SHIFT_ARB 0x2018
|
||||
#define WGL_BLUE_BITS_ARB 0x2019
|
||||
#define WGL_BLUE_SHIFT_ARB 0x201A
|
||||
#define WGL_ALPHA_BITS_ARB 0x201B
|
||||
#define WGL_ALPHA_SHIFT_ARB 0x201C
|
||||
#define WGL_ACCUM_BITS_ARB 0x201D
|
||||
#define WGL_ACCUM_RED_BITS_ARB 0x201E
|
||||
#define WGL_ACCUM_GREEN_BITS_ARB 0x201F
|
||||
#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
|
||||
#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
|
||||
#define WGL_DEPTH_BITS_ARB 0x2022
|
||||
#define WGL_STENCIL_BITS_ARB 0x2023
|
||||
#define WGL_AUX_BUFFERS_ARB 0x2024
|
||||
#define WGL_NO_ACCELERATION_ARB 0x2025
|
||||
#define WGL_GENERIC_ACCELERATION_ARB 0x2026
|
||||
#define WGL_FULL_ACCELERATION_ARB 0x2027
|
||||
#define WGL_SWAP_EXCHANGE_ARB 0x2028
|
||||
#define WGL_SWAP_COPY_ARB 0x2029
|
||||
#define WGL_SWAP_UNDEFINED_ARB 0x202A
|
||||
#define WGL_TYPE_RGBA_ARB 0x202B
|
||||
#define WGL_TYPE_COLORINDEX_ARB 0x202C
|
||||
typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues);
|
||||
typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBFVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, FLOAT *pfValues);
|
||||
typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglGetPixelFormatAttribivARB (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues);
|
||||
BOOL WINAPI wglGetPixelFormatAttribfvARB (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, FLOAT *pfValues);
|
||||
BOOL WINAPI wglChoosePixelFormatARB (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
|
||||
#endif
|
||||
#endif /* WGL_ARB_pixel_format */
|
||||
|
||||
#ifndef WGL_ARB_pixel_format_float
|
||||
#define WGL_ARB_pixel_format_float 1
|
||||
#define WGL_TYPE_RGBA_FLOAT_ARB 0x21A0
|
||||
#endif /* WGL_ARB_pixel_format_float */
|
||||
|
||||
#ifndef WGL_ARB_render_texture
|
||||
#define WGL_ARB_render_texture 1
|
||||
#define WGL_BIND_TO_TEXTURE_RGB_ARB 0x2070
|
||||
#define WGL_BIND_TO_TEXTURE_RGBA_ARB 0x2071
|
||||
#define WGL_TEXTURE_FORMAT_ARB 0x2072
|
||||
#define WGL_TEXTURE_TARGET_ARB 0x2073
|
||||
#define WGL_MIPMAP_TEXTURE_ARB 0x2074
|
||||
#define WGL_TEXTURE_RGB_ARB 0x2075
|
||||
#define WGL_TEXTURE_RGBA_ARB 0x2076
|
||||
#define WGL_NO_TEXTURE_ARB 0x2077
|
||||
#define WGL_TEXTURE_CUBE_MAP_ARB 0x2078
|
||||
#define WGL_TEXTURE_1D_ARB 0x2079
|
||||
#define WGL_TEXTURE_2D_ARB 0x207A
|
||||
#define WGL_MIPMAP_LEVEL_ARB 0x207B
|
||||
#define WGL_CUBE_MAP_FACE_ARB 0x207C
|
||||
#define WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x207D
|
||||
#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x207E
|
||||
#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x207F
|
||||
#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x2080
|
||||
#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x2081
|
||||
#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x2082
|
||||
#define WGL_FRONT_LEFT_ARB 0x2083
|
||||
#define WGL_FRONT_RIGHT_ARB 0x2084
|
||||
#define WGL_BACK_LEFT_ARB 0x2085
|
||||
#define WGL_BACK_RIGHT_ARB 0x2086
|
||||
#define WGL_AUX0_ARB 0x2087
|
||||
#define WGL_AUX1_ARB 0x2088
|
||||
#define WGL_AUX2_ARB 0x2089
|
||||
#define WGL_AUX3_ARB 0x208A
|
||||
#define WGL_AUX4_ARB 0x208B
|
||||
#define WGL_AUX5_ARB 0x208C
|
||||
#define WGL_AUX6_ARB 0x208D
|
||||
#define WGL_AUX7_ARB 0x208E
|
||||
#define WGL_AUX8_ARB 0x208F
|
||||
#define WGL_AUX9_ARB 0x2090
|
||||
typedef BOOL (WINAPI * PFNWGLBINDTEXIMAGEARBPROC) (HPBUFFERARB hPbuffer, int iBuffer);
|
||||
typedef BOOL (WINAPI * PFNWGLRELEASETEXIMAGEARBPROC) (HPBUFFERARB hPbuffer, int iBuffer);
|
||||
typedef BOOL (WINAPI * PFNWGLSETPBUFFERATTRIBARBPROC) (HPBUFFERARB hPbuffer, const int *piAttribList);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglBindTexImageARB (HPBUFFERARB hPbuffer, int iBuffer);
|
||||
BOOL WINAPI wglReleaseTexImageARB (HPBUFFERARB hPbuffer, int iBuffer);
|
||||
BOOL WINAPI wglSetPbufferAttribARB (HPBUFFERARB hPbuffer, const int *piAttribList);
|
||||
#endif
|
||||
#endif /* WGL_ARB_render_texture */
|
||||
|
||||
#ifndef WGL_ARB_robustness_application_isolation
|
||||
#define WGL_ARB_robustness_application_isolation 1
|
||||
#define WGL_CONTEXT_RESET_ISOLATION_BIT_ARB 0x00000008
|
||||
#endif /* WGL_ARB_robustness_application_isolation */
|
||||
|
||||
#ifndef WGL_ARB_robustness_share_group_isolation
|
||||
#define WGL_ARB_robustness_share_group_isolation 1
|
||||
#endif /* WGL_ARB_robustness_share_group_isolation */
|
||||
|
||||
#ifndef WGL_3DFX_multisample
|
||||
#define WGL_3DFX_multisample 1
|
||||
#define WGL_SAMPLE_BUFFERS_3DFX 0x2060
|
||||
#define WGL_SAMPLES_3DFX 0x2061
|
||||
#endif /* WGL_3DFX_multisample */
|
||||
|
||||
#ifndef WGL_3DL_stereo_control
|
||||
#define WGL_3DL_stereo_control 1
|
||||
#define WGL_STEREO_EMITTER_ENABLE_3DL 0x2055
|
||||
#define WGL_STEREO_EMITTER_DISABLE_3DL 0x2056
|
||||
#define WGL_STEREO_POLARITY_NORMAL_3DL 0x2057
|
||||
#define WGL_STEREO_POLARITY_INVERT_3DL 0x2058
|
||||
typedef BOOL (WINAPI * PFNWGLSETSTEREOEMITTERSTATE3DLPROC) (HDC hDC, UINT uState);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglSetStereoEmitterState3DL (HDC hDC, UINT uState);
|
||||
#endif
|
||||
#endif /* WGL_3DL_stereo_control */
|
||||
|
||||
#ifndef WGL_AMD_gpu_association
|
||||
#define WGL_AMD_gpu_association 1
|
||||
#define WGL_GPU_VENDOR_AMD 0x1F00
|
||||
#define WGL_GPU_RENDERER_STRING_AMD 0x1F01
|
||||
#define WGL_GPU_OPENGL_VERSION_STRING_AMD 0x1F02
|
||||
#define WGL_GPU_FASTEST_TARGET_GPUS_AMD 0x21A2
|
||||
#define WGL_GPU_RAM_AMD 0x21A3
|
||||
#define WGL_GPU_CLOCK_AMD 0x21A4
|
||||
#define WGL_GPU_NUM_PIPES_AMD 0x21A5
|
||||
#define WGL_GPU_NUM_SIMD_AMD 0x21A6
|
||||
#define WGL_GPU_NUM_RB_AMD 0x21A7
|
||||
#define WGL_GPU_NUM_SPI_AMD 0x21A8
|
||||
typedef UINT (WINAPI * PFNWGLGETGPUIDSAMDPROC) (UINT maxCount, UINT *ids);
|
||||
typedef INT (WINAPI * PFNWGLGETGPUINFOAMDPROC) (UINT id, int property, GLenum dataType, UINT size, void *data);
|
||||
typedef UINT (WINAPI * PFNWGLGETCONTEXTGPUIDAMDPROC) (HGLRC hglrc);
|
||||
typedef HGLRC (WINAPI * PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC) (UINT id);
|
||||
typedef HGLRC (WINAPI * PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC) (UINT id, HGLRC hShareContext, const int *attribList);
|
||||
typedef BOOL (WINAPI * PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC) (HGLRC hglrc);
|
||||
typedef BOOL (WINAPI * PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC) (HGLRC hglrc);
|
||||
typedef HGLRC (WINAPI * PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC) (void);
|
||||
typedef VOID (WINAPI * PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC) (HGLRC dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
UINT WINAPI wglGetGPUIDsAMD (UINT maxCount, UINT *ids);
|
||||
INT WINAPI wglGetGPUInfoAMD (UINT id, int property, GLenum dataType, UINT size, void *data);
|
||||
UINT WINAPI wglGetContextGPUIDAMD (HGLRC hglrc);
|
||||
HGLRC WINAPI wglCreateAssociatedContextAMD (UINT id);
|
||||
HGLRC WINAPI wglCreateAssociatedContextAttribsAMD (UINT id, HGLRC hShareContext, const int *attribList);
|
||||
BOOL WINAPI wglDeleteAssociatedContextAMD (HGLRC hglrc);
|
||||
BOOL WINAPI wglMakeAssociatedContextCurrentAMD (HGLRC hglrc);
|
||||
HGLRC WINAPI wglGetCurrentAssociatedContextAMD (void);
|
||||
VOID WINAPI wglBlitContextFramebufferAMD (HGLRC dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
|
||||
#endif
|
||||
#endif /* WGL_AMD_gpu_association */
|
||||
|
||||
#ifndef WGL_ATI_pixel_format_float
|
||||
#define WGL_ATI_pixel_format_float 1
|
||||
#define WGL_TYPE_RGBA_FLOAT_ATI 0x21A0
|
||||
#endif /* WGL_ATI_pixel_format_float */
|
||||
|
||||
#ifndef WGL_EXT_create_context_es2_profile
|
||||
#define WGL_EXT_create_context_es2_profile 1
|
||||
#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
|
||||
#endif /* WGL_EXT_create_context_es2_profile */
|
||||
|
||||
#ifndef WGL_EXT_create_context_es_profile
|
||||
#define WGL_EXT_create_context_es_profile 1
|
||||
#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004
|
||||
#endif /* WGL_EXT_create_context_es_profile */
|
||||
|
||||
#ifndef WGL_EXT_depth_float
|
||||
#define WGL_EXT_depth_float 1
|
||||
#define WGL_DEPTH_FLOAT_EXT 0x2040
|
||||
#endif /* WGL_EXT_depth_float */
|
||||
|
||||
#ifndef WGL_EXT_display_color_table
|
||||
#define WGL_EXT_display_color_table 1
|
||||
typedef GLboolean (WINAPI * PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC) (GLushort id);
|
||||
typedef GLboolean (WINAPI * PFNWGLLOADDISPLAYCOLORTABLEEXTPROC) (const GLushort *table, GLuint length);
|
||||
typedef GLboolean (WINAPI * PFNWGLBINDDISPLAYCOLORTABLEEXTPROC) (GLushort id);
|
||||
typedef VOID (WINAPI * PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC) (GLushort id);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
GLboolean WINAPI wglCreateDisplayColorTableEXT (GLushort id);
|
||||
GLboolean WINAPI wglLoadDisplayColorTableEXT (const GLushort *table, GLuint length);
|
||||
GLboolean WINAPI wglBindDisplayColorTableEXT (GLushort id);
|
||||
VOID WINAPI wglDestroyDisplayColorTableEXT (GLushort id);
|
||||
#endif
|
||||
#endif /* WGL_EXT_display_color_table */
|
||||
|
||||
#ifndef WGL_EXT_extensions_string
|
||||
#define WGL_EXT_extensions_string 1
|
||||
typedef const char *(WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC) (void);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
const char *WINAPI wglGetExtensionsStringEXT (void);
|
||||
#endif
|
||||
#endif /* WGL_EXT_extensions_string */
|
||||
|
||||
#ifndef WGL_EXT_framebuffer_sRGB
|
||||
#define WGL_EXT_framebuffer_sRGB 1
|
||||
#define WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x20A9
|
||||
#endif /* WGL_EXT_framebuffer_sRGB */
|
||||
|
||||
#ifndef WGL_EXT_make_current_read
|
||||
#define WGL_EXT_make_current_read 1
|
||||
#define ERROR_INVALID_PIXEL_TYPE_EXT 0x2043
|
||||
typedef BOOL (WINAPI * PFNWGLMAKECONTEXTCURRENTEXTPROC) (HDC hDrawDC, HDC hReadDC, HGLRC hglrc);
|
||||
typedef HDC (WINAPI * PFNWGLGETCURRENTREADDCEXTPROC) (void);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglMakeContextCurrentEXT (HDC hDrawDC, HDC hReadDC, HGLRC hglrc);
|
||||
HDC WINAPI wglGetCurrentReadDCEXT (void);
|
||||
#endif
|
||||
#endif /* WGL_EXT_make_current_read */
|
||||
|
||||
#ifndef WGL_EXT_multisample
|
||||
#define WGL_EXT_multisample 1
|
||||
#define WGL_SAMPLE_BUFFERS_EXT 0x2041
|
||||
#define WGL_SAMPLES_EXT 0x2042
|
||||
#endif /* WGL_EXT_multisample */
|
||||
|
||||
#ifndef WGL_EXT_pbuffer
|
||||
#define WGL_EXT_pbuffer 1
|
||||
DECLARE_HANDLE(HPBUFFEREXT);
|
||||
#define WGL_DRAW_TO_PBUFFER_EXT 0x202D
|
||||
#define WGL_MAX_PBUFFER_PIXELS_EXT 0x202E
|
||||
#define WGL_MAX_PBUFFER_WIDTH_EXT 0x202F
|
||||
#define WGL_MAX_PBUFFER_HEIGHT_EXT 0x2030
|
||||
#define WGL_OPTIMAL_PBUFFER_WIDTH_EXT 0x2031
|
||||
#define WGL_OPTIMAL_PBUFFER_HEIGHT_EXT 0x2032
|
||||
#define WGL_PBUFFER_LARGEST_EXT 0x2033
|
||||
#define WGL_PBUFFER_WIDTH_EXT 0x2034
|
||||
#define WGL_PBUFFER_HEIGHT_EXT 0x2035
|
||||
typedef HPBUFFEREXT (WINAPI * PFNWGLCREATEPBUFFEREXTPROC) (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList);
|
||||
typedef HDC (WINAPI * PFNWGLGETPBUFFERDCEXTPROC) (HPBUFFEREXT hPbuffer);
|
||||
typedef int (WINAPI * PFNWGLRELEASEPBUFFERDCEXTPROC) (HPBUFFEREXT hPbuffer, HDC hDC);
|
||||
typedef BOOL (WINAPI * PFNWGLDESTROYPBUFFEREXTPROC) (HPBUFFEREXT hPbuffer);
|
||||
typedef BOOL (WINAPI * PFNWGLQUERYPBUFFEREXTPROC) (HPBUFFEREXT hPbuffer, int iAttribute, int *piValue);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
HPBUFFEREXT WINAPI wglCreatePbufferEXT (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList);
|
||||
HDC WINAPI wglGetPbufferDCEXT (HPBUFFEREXT hPbuffer);
|
||||
int WINAPI wglReleasePbufferDCEXT (HPBUFFEREXT hPbuffer, HDC hDC);
|
||||
BOOL WINAPI wglDestroyPbufferEXT (HPBUFFEREXT hPbuffer);
|
||||
BOOL WINAPI wglQueryPbufferEXT (HPBUFFEREXT hPbuffer, int iAttribute, int *piValue);
|
||||
#endif
|
||||
#endif /* WGL_EXT_pbuffer */
|
||||
|
||||
#ifndef WGL_EXT_pixel_format
|
||||
#define WGL_EXT_pixel_format 1
|
||||
#define WGL_NUMBER_PIXEL_FORMATS_EXT 0x2000
|
||||
#define WGL_DRAW_TO_WINDOW_EXT 0x2001
|
||||
#define WGL_DRAW_TO_BITMAP_EXT 0x2002
|
||||
#define WGL_ACCELERATION_EXT 0x2003
|
||||
#define WGL_NEED_PALETTE_EXT 0x2004
|
||||
#define WGL_NEED_SYSTEM_PALETTE_EXT 0x2005
|
||||
#define WGL_SWAP_LAYER_BUFFERS_EXT 0x2006
|
||||
#define WGL_SWAP_METHOD_EXT 0x2007
|
||||
#define WGL_NUMBER_OVERLAYS_EXT 0x2008
|
||||
#define WGL_NUMBER_UNDERLAYS_EXT 0x2009
|
||||
#define WGL_TRANSPARENT_EXT 0x200A
|
||||
#define WGL_TRANSPARENT_VALUE_EXT 0x200B
|
||||
#define WGL_SHARE_DEPTH_EXT 0x200C
|
||||
#define WGL_SHARE_STENCIL_EXT 0x200D
|
||||
#define WGL_SHARE_ACCUM_EXT 0x200E
|
||||
#define WGL_SUPPORT_GDI_EXT 0x200F
|
||||
#define WGL_SUPPORT_OPENGL_EXT 0x2010
|
||||
#define WGL_DOUBLE_BUFFER_EXT 0x2011
|
||||
#define WGL_STEREO_EXT 0x2012
|
||||
#define WGL_PIXEL_TYPE_EXT 0x2013
|
||||
#define WGL_COLOR_BITS_EXT 0x2014
|
||||
#define WGL_RED_BITS_EXT 0x2015
|
||||
#define WGL_RED_SHIFT_EXT 0x2016
|
||||
#define WGL_GREEN_BITS_EXT 0x2017
|
||||
#define WGL_GREEN_SHIFT_EXT 0x2018
|
||||
#define WGL_BLUE_BITS_EXT 0x2019
|
||||
#define WGL_BLUE_SHIFT_EXT 0x201A
|
||||
#define WGL_ALPHA_BITS_EXT 0x201B
|
||||
#define WGL_ALPHA_SHIFT_EXT 0x201C
|
||||
#define WGL_ACCUM_BITS_EXT 0x201D
|
||||
#define WGL_ACCUM_RED_BITS_EXT 0x201E
|
||||
#define WGL_ACCUM_GREEN_BITS_EXT 0x201F
|
||||
#define WGL_ACCUM_BLUE_BITS_EXT 0x2020
|
||||
#define WGL_ACCUM_ALPHA_BITS_EXT 0x2021
|
||||
#define WGL_DEPTH_BITS_EXT 0x2022
|
||||
#define WGL_STENCIL_BITS_EXT 0x2023
|
||||
#define WGL_AUX_BUFFERS_EXT 0x2024
|
||||
#define WGL_NO_ACCELERATION_EXT 0x2025
|
||||
#define WGL_GENERIC_ACCELERATION_EXT 0x2026
|
||||
#define WGL_FULL_ACCELERATION_EXT 0x2027
|
||||
#define WGL_SWAP_EXCHANGE_EXT 0x2028
|
||||
#define WGL_SWAP_COPY_EXT 0x2029
|
||||
#define WGL_SWAP_UNDEFINED_EXT 0x202A
|
||||
#define WGL_TYPE_RGBA_EXT 0x202B
|
||||
#define WGL_TYPE_COLORINDEX_EXT 0x202C
|
||||
typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVEXTPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, int *piValues);
|
||||
typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBFVEXTPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, FLOAT *pfValues);
|
||||
typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATEXTPROC) (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglGetPixelFormatAttribivEXT (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, int *piValues);
|
||||
BOOL WINAPI wglGetPixelFormatAttribfvEXT (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, FLOAT *pfValues);
|
||||
BOOL WINAPI wglChoosePixelFormatEXT (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
|
||||
#endif
|
||||
#endif /* WGL_EXT_pixel_format */
|
||||
|
||||
#ifndef WGL_EXT_pixel_format_packed_float
|
||||
#define WGL_EXT_pixel_format_packed_float 1
|
||||
#define WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT 0x20A8
|
||||
#endif /* WGL_EXT_pixel_format_packed_float */
|
||||
|
||||
#ifndef WGL_EXT_swap_control
|
||||
#define WGL_EXT_swap_control 1
|
||||
typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC) (int interval);
|
||||
typedef int (WINAPI * PFNWGLGETSWAPINTERVALEXTPROC) (void);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglSwapIntervalEXT (int interval);
|
||||
int WINAPI wglGetSwapIntervalEXT (void);
|
||||
#endif
|
||||
#endif /* WGL_EXT_swap_control */
|
||||
|
||||
#ifndef WGL_EXT_swap_control_tear
|
||||
#define WGL_EXT_swap_control_tear 1
|
||||
#endif /* WGL_EXT_swap_control_tear */
|
||||
|
||||
#ifndef WGL_I3D_digital_video_control
|
||||
#define WGL_I3D_digital_video_control 1
|
||||
#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_FRAMEBUFFER_I3D 0x2050
|
||||
#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_VALUE_I3D 0x2051
|
||||
#define WGL_DIGITAL_VIDEO_CURSOR_INCLUDED_I3D 0x2052
|
||||
#define WGL_DIGITAL_VIDEO_GAMMA_CORRECTED_I3D 0x2053
|
||||
typedef BOOL (WINAPI * PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC) (HDC hDC, int iAttribute, int *piValue);
|
||||
typedef BOOL (WINAPI * PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC) (HDC hDC, int iAttribute, const int *piValue);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglGetDigitalVideoParametersI3D (HDC hDC, int iAttribute, int *piValue);
|
||||
BOOL WINAPI wglSetDigitalVideoParametersI3D (HDC hDC, int iAttribute, const int *piValue);
|
||||
#endif
|
||||
#endif /* WGL_I3D_digital_video_control */
|
||||
|
||||
#ifndef WGL_I3D_gamma
|
||||
#define WGL_I3D_gamma 1
|
||||
#define WGL_GAMMA_TABLE_SIZE_I3D 0x204E
|
||||
#define WGL_GAMMA_EXCLUDE_DESKTOP_I3D 0x204F
|
||||
typedef BOOL (WINAPI * PFNWGLGETGAMMATABLEPARAMETERSI3DPROC) (HDC hDC, int iAttribute, int *piValue);
|
||||
typedef BOOL (WINAPI * PFNWGLSETGAMMATABLEPARAMETERSI3DPROC) (HDC hDC, int iAttribute, const int *piValue);
|
||||
typedef BOOL (WINAPI * PFNWGLGETGAMMATABLEI3DPROC) (HDC hDC, int iEntries, USHORT *puRed, USHORT *puGreen, USHORT *puBlue);
|
||||
typedef BOOL (WINAPI * PFNWGLSETGAMMATABLEI3DPROC) (HDC hDC, int iEntries, const USHORT *puRed, const USHORT *puGreen, const USHORT *puBlue);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglGetGammaTableParametersI3D (HDC hDC, int iAttribute, int *piValue);
|
||||
BOOL WINAPI wglSetGammaTableParametersI3D (HDC hDC, int iAttribute, const int *piValue);
|
||||
BOOL WINAPI wglGetGammaTableI3D (HDC hDC, int iEntries, USHORT *puRed, USHORT *puGreen, USHORT *puBlue);
|
||||
BOOL WINAPI wglSetGammaTableI3D (HDC hDC, int iEntries, const USHORT *puRed, const USHORT *puGreen, const USHORT *puBlue);
|
||||
#endif
|
||||
#endif /* WGL_I3D_gamma */
|
||||
|
||||
#ifndef WGL_I3D_genlock
|
||||
#define WGL_I3D_genlock 1
|
||||
#define WGL_GENLOCK_SOURCE_MULTIVIEW_I3D 0x2044
|
||||
#define WGL_GENLOCK_SOURCE_EXTERNAL_SYNC_I3D 0x2045
|
||||
#define WGL_GENLOCK_SOURCE_EXTERNAL_FIELD_I3D 0x2046
|
||||
#define WGL_GENLOCK_SOURCE_EXTERNAL_TTL_I3D 0x2047
|
||||
#define WGL_GENLOCK_SOURCE_DIGITAL_SYNC_I3D 0x2048
|
||||
#define WGL_GENLOCK_SOURCE_DIGITAL_FIELD_I3D 0x2049
|
||||
#define WGL_GENLOCK_SOURCE_EDGE_FALLING_I3D 0x204A
|
||||
#define WGL_GENLOCK_SOURCE_EDGE_RISING_I3D 0x204B
|
||||
#define WGL_GENLOCK_SOURCE_EDGE_BOTH_I3D 0x204C
|
||||
typedef BOOL (WINAPI * PFNWGLENABLEGENLOCKI3DPROC) (HDC hDC);
|
||||
typedef BOOL (WINAPI * PFNWGLDISABLEGENLOCKI3DPROC) (HDC hDC);
|
||||
typedef BOOL (WINAPI * PFNWGLISENABLEDGENLOCKI3DPROC) (HDC hDC, BOOL *pFlag);
|
||||
typedef BOOL (WINAPI * PFNWGLGENLOCKSOURCEI3DPROC) (HDC hDC, UINT uSource);
|
||||
typedef BOOL (WINAPI * PFNWGLGETGENLOCKSOURCEI3DPROC) (HDC hDC, UINT *uSource);
|
||||
typedef BOOL (WINAPI * PFNWGLGENLOCKSOURCEEDGEI3DPROC) (HDC hDC, UINT uEdge);
|
||||
typedef BOOL (WINAPI * PFNWGLGETGENLOCKSOURCEEDGEI3DPROC) (HDC hDC, UINT *uEdge);
|
||||
typedef BOOL (WINAPI * PFNWGLGENLOCKSAMPLERATEI3DPROC) (HDC hDC, UINT uRate);
|
||||
typedef BOOL (WINAPI * PFNWGLGETGENLOCKSAMPLERATEI3DPROC) (HDC hDC, UINT *uRate);
|
||||
typedef BOOL (WINAPI * PFNWGLGENLOCKSOURCEDELAYI3DPROC) (HDC hDC, UINT uDelay);
|
||||
typedef BOOL (WINAPI * PFNWGLGETGENLOCKSOURCEDELAYI3DPROC) (HDC hDC, UINT *uDelay);
|
||||
typedef BOOL (WINAPI * PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC) (HDC hDC, UINT *uMaxLineDelay, UINT *uMaxPixelDelay);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglEnableGenlockI3D (HDC hDC);
|
||||
BOOL WINAPI wglDisableGenlockI3D (HDC hDC);
|
||||
BOOL WINAPI wglIsEnabledGenlockI3D (HDC hDC, BOOL *pFlag);
|
||||
BOOL WINAPI wglGenlockSourceI3D (HDC hDC, UINT uSource);
|
||||
BOOL WINAPI wglGetGenlockSourceI3D (HDC hDC, UINT *uSource);
|
||||
BOOL WINAPI wglGenlockSourceEdgeI3D (HDC hDC, UINT uEdge);
|
||||
BOOL WINAPI wglGetGenlockSourceEdgeI3D (HDC hDC, UINT *uEdge);
|
||||
BOOL WINAPI wglGenlockSampleRateI3D (HDC hDC, UINT uRate);
|
||||
BOOL WINAPI wglGetGenlockSampleRateI3D (HDC hDC, UINT *uRate);
|
||||
BOOL WINAPI wglGenlockSourceDelayI3D (HDC hDC, UINT uDelay);
|
||||
BOOL WINAPI wglGetGenlockSourceDelayI3D (HDC hDC, UINT *uDelay);
|
||||
BOOL WINAPI wglQueryGenlockMaxSourceDelayI3D (HDC hDC, UINT *uMaxLineDelay, UINT *uMaxPixelDelay);
|
||||
#endif
|
||||
#endif /* WGL_I3D_genlock */
|
||||
|
||||
#ifndef WGL_I3D_image_buffer
|
||||
#define WGL_I3D_image_buffer 1
|
||||
#define WGL_IMAGE_BUFFER_MIN_ACCESS_I3D 0x00000001
|
||||
#define WGL_IMAGE_BUFFER_LOCK_I3D 0x00000002
|
||||
typedef LPVOID (WINAPI * PFNWGLCREATEIMAGEBUFFERI3DPROC) (HDC hDC, DWORD dwSize, UINT uFlags);
|
||||
typedef BOOL (WINAPI * PFNWGLDESTROYIMAGEBUFFERI3DPROC) (HDC hDC, LPVOID pAddress);
|
||||
typedef BOOL (WINAPI * PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC) (HDC hDC, const HANDLE *pEvent, const LPVOID *pAddress, const DWORD *pSize, UINT count);
|
||||
typedef BOOL (WINAPI * PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC) (HDC hDC, const LPVOID *pAddress, UINT count);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
LPVOID WINAPI wglCreateImageBufferI3D (HDC hDC, DWORD dwSize, UINT uFlags);
|
||||
BOOL WINAPI wglDestroyImageBufferI3D (HDC hDC, LPVOID pAddress);
|
||||
BOOL WINAPI wglAssociateImageBufferEventsI3D (HDC hDC, const HANDLE *pEvent, const LPVOID *pAddress, const DWORD *pSize, UINT count);
|
||||
BOOL WINAPI wglReleaseImageBufferEventsI3D (HDC hDC, const LPVOID *pAddress, UINT count);
|
||||
#endif
|
||||
#endif /* WGL_I3D_image_buffer */
|
||||
|
||||
#ifndef WGL_I3D_swap_frame_lock
|
||||
#define WGL_I3D_swap_frame_lock 1
|
||||
typedef BOOL (WINAPI * PFNWGLENABLEFRAMELOCKI3DPROC) (void);
|
||||
typedef BOOL (WINAPI * PFNWGLDISABLEFRAMELOCKI3DPROC) (void);
|
||||
typedef BOOL (WINAPI * PFNWGLISENABLEDFRAMELOCKI3DPROC) (BOOL *pFlag);
|
||||
typedef BOOL (WINAPI * PFNWGLQUERYFRAMELOCKMASTERI3DPROC) (BOOL *pFlag);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglEnableFrameLockI3D (void);
|
||||
BOOL WINAPI wglDisableFrameLockI3D (void);
|
||||
BOOL WINAPI wglIsEnabledFrameLockI3D (BOOL *pFlag);
|
||||
BOOL WINAPI wglQueryFrameLockMasterI3D (BOOL *pFlag);
|
||||
#endif
|
||||
#endif /* WGL_I3D_swap_frame_lock */
|
||||
|
||||
#ifndef WGL_I3D_swap_frame_usage
|
||||
#define WGL_I3D_swap_frame_usage 1
|
||||
typedef BOOL (WINAPI * PFNWGLGETFRAMEUSAGEI3DPROC) (float *pUsage);
|
||||
typedef BOOL (WINAPI * PFNWGLBEGINFRAMETRACKINGI3DPROC) (void);
|
||||
typedef BOOL (WINAPI * PFNWGLENDFRAMETRACKINGI3DPROC) (void);
|
||||
typedef BOOL (WINAPI * PFNWGLQUERYFRAMETRACKINGI3DPROC) (DWORD *pFrameCount, DWORD *pMissedFrames, float *pLastMissedUsage);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglGetFrameUsageI3D (float *pUsage);
|
||||
BOOL WINAPI wglBeginFrameTrackingI3D (void);
|
||||
BOOL WINAPI wglEndFrameTrackingI3D (void);
|
||||
BOOL WINAPI wglQueryFrameTrackingI3D (DWORD *pFrameCount, DWORD *pMissedFrames, float *pLastMissedUsage);
|
||||
#endif
|
||||
#endif /* WGL_I3D_swap_frame_usage */
|
||||
|
||||
#ifndef WGL_NV_DX_interop
|
||||
#define WGL_NV_DX_interop 1
|
||||
#define WGL_ACCESS_READ_ONLY_NV 0x00000000
|
||||
#define WGL_ACCESS_READ_WRITE_NV 0x00000001
|
||||
#define WGL_ACCESS_WRITE_DISCARD_NV 0x00000002
|
||||
typedef BOOL (WINAPI * PFNWGLDXSETRESOURCESHAREHANDLENVPROC) (void *dxObject, HANDLE shareHandle);
|
||||
typedef HANDLE (WINAPI * PFNWGLDXOPENDEVICENVPROC) (void *dxDevice);
|
||||
typedef BOOL (WINAPI * PFNWGLDXCLOSEDEVICENVPROC) (HANDLE hDevice);
|
||||
typedef HANDLE (WINAPI * PFNWGLDXREGISTEROBJECTNVPROC) (HANDLE hDevice, void *dxObject, GLuint name, GLenum type, GLenum access);
|
||||
typedef BOOL (WINAPI * PFNWGLDXUNREGISTEROBJECTNVPROC) (HANDLE hDevice, HANDLE hObject);
|
||||
typedef BOOL (WINAPI * PFNWGLDXOBJECTACCESSNVPROC) (HANDLE hObject, GLenum access);
|
||||
typedef BOOL (WINAPI * PFNWGLDXLOCKOBJECTSNVPROC) (HANDLE hDevice, GLint count, HANDLE *hObjects);
|
||||
typedef BOOL (WINAPI * PFNWGLDXUNLOCKOBJECTSNVPROC) (HANDLE hDevice, GLint count, HANDLE *hObjects);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglDXSetResourceShareHandleNV (void *dxObject, HANDLE shareHandle);
|
||||
HANDLE WINAPI wglDXOpenDeviceNV (void *dxDevice);
|
||||
BOOL WINAPI wglDXCloseDeviceNV (HANDLE hDevice);
|
||||
HANDLE WINAPI wglDXRegisterObjectNV (HANDLE hDevice, void *dxObject, GLuint name, GLenum type, GLenum access);
|
||||
BOOL WINAPI wglDXUnregisterObjectNV (HANDLE hDevice, HANDLE hObject);
|
||||
BOOL WINAPI wglDXObjectAccessNV (HANDLE hObject, GLenum access);
|
||||
BOOL WINAPI wglDXLockObjectsNV (HANDLE hDevice, GLint count, HANDLE *hObjects);
|
||||
BOOL WINAPI wglDXUnlockObjectsNV (HANDLE hDevice, GLint count, HANDLE *hObjects);
|
||||
#endif
|
||||
#endif /* WGL_NV_DX_interop */
|
||||
|
||||
#ifndef WGL_NV_DX_interop2
|
||||
#define WGL_NV_DX_interop2 1
|
||||
#endif /* WGL_NV_DX_interop2 */
|
||||
|
||||
#ifndef WGL_NV_copy_image
|
||||
#define WGL_NV_copy_image 1
|
||||
typedef BOOL (WINAPI * PFNWGLCOPYIMAGESUBDATANVPROC) (HGLRC hSrcRC, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, HGLRC hDstRC, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglCopyImageSubDataNV (HGLRC hSrcRC, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, HGLRC hDstRC, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth);
|
||||
#endif
|
||||
#endif /* WGL_NV_copy_image */
|
||||
|
||||
#ifndef WGL_NV_float_buffer
|
||||
#define WGL_NV_float_buffer 1
|
||||
#define WGL_FLOAT_COMPONENTS_NV 0x20B0
|
||||
#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_R_NV 0x20B1
|
||||
#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RG_NV 0x20B2
|
||||
#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGB_NV 0x20B3
|
||||
#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGBA_NV 0x20B4
|
||||
#define WGL_TEXTURE_FLOAT_R_NV 0x20B5
|
||||
#define WGL_TEXTURE_FLOAT_RG_NV 0x20B6
|
||||
#define WGL_TEXTURE_FLOAT_RGB_NV 0x20B7
|
||||
#define WGL_TEXTURE_FLOAT_RGBA_NV 0x20B8
|
||||
#endif /* WGL_NV_float_buffer */
|
||||
|
||||
#ifndef WGL_NV_gpu_affinity
|
||||
#define WGL_NV_gpu_affinity 1
|
||||
DECLARE_HANDLE(HGPUNV);
|
||||
struct _GPU_DEVICE {
|
||||
DWORD cb;
|
||||
CHAR DeviceName[32];
|
||||
CHAR DeviceString[128];
|
||||
DWORD Flags;
|
||||
RECT rcVirtualScreen;
|
||||
};
|
||||
typedef struct _GPU_DEVICE *PGPU_DEVICE;
|
||||
#define ERROR_INCOMPATIBLE_AFFINITY_MASKS_NV 0x20D0
|
||||
#define ERROR_MISSING_AFFINITY_MASK_NV 0x20D1
|
||||
typedef BOOL (WINAPI * PFNWGLENUMGPUSNVPROC) (UINT iGpuIndex, HGPUNV *phGpu);
|
||||
typedef BOOL (WINAPI * PFNWGLENUMGPUDEVICESNVPROC) (HGPUNV hGpu, UINT iDeviceIndex, PGPU_DEVICE lpGpuDevice);
|
||||
typedef HDC (WINAPI * PFNWGLCREATEAFFINITYDCNVPROC) (const HGPUNV *phGpuList);
|
||||
typedef BOOL (WINAPI * PFNWGLENUMGPUSFROMAFFINITYDCNVPROC) (HDC hAffinityDC, UINT iGpuIndex, HGPUNV *hGpu);
|
||||
typedef BOOL (WINAPI * PFNWGLDELETEDCNVPROC) (HDC hdc);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglEnumGpusNV (UINT iGpuIndex, HGPUNV *phGpu);
|
||||
BOOL WINAPI wglEnumGpuDevicesNV (HGPUNV hGpu, UINT iDeviceIndex, PGPU_DEVICE lpGpuDevice);
|
||||
HDC WINAPI wglCreateAffinityDCNV (const HGPUNV *phGpuList);
|
||||
BOOL WINAPI wglEnumGpusFromAffinityDCNV (HDC hAffinityDC, UINT iGpuIndex, HGPUNV *hGpu);
|
||||
BOOL WINAPI wglDeleteDCNV (HDC hdc);
|
||||
#endif
|
||||
#endif /* WGL_NV_gpu_affinity */
|
||||
|
||||
#ifndef WGL_NV_multisample_coverage
|
||||
#define WGL_NV_multisample_coverage 1
|
||||
#define WGL_COVERAGE_SAMPLES_NV 0x2042
|
||||
#define WGL_COLOR_SAMPLES_NV 0x20B9
|
||||
#endif /* WGL_NV_multisample_coverage */
|
||||
|
||||
#ifndef WGL_NV_present_video
|
||||
#define WGL_NV_present_video 1
|
||||
DECLARE_HANDLE(HVIDEOOUTPUTDEVICENV);
|
||||
#define WGL_NUM_VIDEO_SLOTS_NV 0x20F0
|
||||
typedef int (WINAPI * PFNWGLENUMERATEVIDEODEVICESNVPROC) (HDC hDC, HVIDEOOUTPUTDEVICENV *phDeviceList);
|
||||
typedef BOOL (WINAPI * PFNWGLBINDVIDEODEVICENVPROC) (HDC hDC, unsigned int uVideoSlot, HVIDEOOUTPUTDEVICENV hVideoDevice, const int *piAttribList);
|
||||
typedef BOOL (WINAPI * PFNWGLQUERYCURRENTCONTEXTNVPROC) (int iAttribute, int *piValue);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
int WINAPI wglEnumerateVideoDevicesNV (HDC hDC, HVIDEOOUTPUTDEVICENV *phDeviceList);
|
||||
BOOL WINAPI wglBindVideoDeviceNV (HDC hDC, unsigned int uVideoSlot, HVIDEOOUTPUTDEVICENV hVideoDevice, const int *piAttribList);
|
||||
BOOL WINAPI wglQueryCurrentContextNV (int iAttribute, int *piValue);
|
||||
#endif
|
||||
#endif /* WGL_NV_present_video */
|
||||
|
||||
#ifndef WGL_NV_render_depth_texture
|
||||
#define WGL_NV_render_depth_texture 1
|
||||
#define WGL_BIND_TO_TEXTURE_DEPTH_NV 0x20A3
|
||||
#define WGL_BIND_TO_TEXTURE_RECTANGLE_DEPTH_NV 0x20A4
|
||||
#define WGL_DEPTH_TEXTURE_FORMAT_NV 0x20A5
|
||||
#define WGL_TEXTURE_DEPTH_COMPONENT_NV 0x20A6
|
||||
#define WGL_DEPTH_COMPONENT_NV 0x20A7
|
||||
#endif /* WGL_NV_render_depth_texture */
|
||||
|
||||
#ifndef WGL_NV_render_texture_rectangle
|
||||
#define WGL_NV_render_texture_rectangle 1
|
||||
#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGB_NV 0x20A0
|
||||
#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGBA_NV 0x20A1
|
||||
#define WGL_TEXTURE_RECTANGLE_NV 0x20A2
|
||||
#endif /* WGL_NV_render_texture_rectangle */
|
||||
|
||||
#ifndef WGL_NV_swap_group
|
||||
#define WGL_NV_swap_group 1
|
||||
typedef BOOL (WINAPI * PFNWGLJOINSWAPGROUPNVPROC) (HDC hDC, GLuint group);
|
||||
typedef BOOL (WINAPI * PFNWGLBINDSWAPBARRIERNVPROC) (GLuint group, GLuint barrier);
|
||||
typedef BOOL (WINAPI * PFNWGLQUERYSWAPGROUPNVPROC) (HDC hDC, GLuint *group, GLuint *barrier);
|
||||
typedef BOOL (WINAPI * PFNWGLQUERYMAXSWAPGROUPSNVPROC) (HDC hDC, GLuint *maxGroups, GLuint *maxBarriers);
|
||||
typedef BOOL (WINAPI * PFNWGLQUERYFRAMECOUNTNVPROC) (HDC hDC, GLuint *count);
|
||||
typedef BOOL (WINAPI * PFNWGLRESETFRAMECOUNTNVPROC) (HDC hDC);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglJoinSwapGroupNV (HDC hDC, GLuint group);
|
||||
BOOL WINAPI wglBindSwapBarrierNV (GLuint group, GLuint barrier);
|
||||
BOOL WINAPI wglQuerySwapGroupNV (HDC hDC, GLuint *group, GLuint *barrier);
|
||||
BOOL WINAPI wglQueryMaxSwapGroupsNV (HDC hDC, GLuint *maxGroups, GLuint *maxBarriers);
|
||||
BOOL WINAPI wglQueryFrameCountNV (HDC hDC, GLuint *count);
|
||||
BOOL WINAPI wglResetFrameCountNV (HDC hDC);
|
||||
#endif
|
||||
#endif /* WGL_NV_swap_group */
|
||||
|
||||
#ifndef WGL_NV_vertex_array_range
|
||||
#define WGL_NV_vertex_array_range 1
|
||||
typedef void *(WINAPI * PFNWGLALLOCATEMEMORYNVPROC) (GLsizei size, GLfloat readfreq, GLfloat writefreq, GLfloat priority);
|
||||
typedef void (WINAPI * PFNWGLFREEMEMORYNVPROC) (void *pointer);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
void *WINAPI wglAllocateMemoryNV (GLsizei size, GLfloat readfreq, GLfloat writefreq, GLfloat priority);
|
||||
void WINAPI wglFreeMemoryNV (void *pointer);
|
||||
#endif
|
||||
#endif /* WGL_NV_vertex_array_range */
|
||||
|
||||
#ifndef WGL_NV_video_capture
|
||||
#define WGL_NV_video_capture 1
|
||||
DECLARE_HANDLE(HVIDEOINPUTDEVICENV);
|
||||
#define WGL_UNIQUE_ID_NV 0x20CE
|
||||
#define WGL_NUM_VIDEO_CAPTURE_SLOTS_NV 0x20CF
|
||||
typedef BOOL (WINAPI * PFNWGLBINDVIDEOCAPTUREDEVICENVPROC) (UINT uVideoSlot, HVIDEOINPUTDEVICENV hDevice);
|
||||
typedef UINT (WINAPI * PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC) (HDC hDc, HVIDEOINPUTDEVICENV *phDeviceList);
|
||||
typedef BOOL (WINAPI * PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC) (HDC hDc, HVIDEOINPUTDEVICENV hDevice);
|
||||
typedef BOOL (WINAPI * PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC) (HDC hDc, HVIDEOINPUTDEVICENV hDevice, int iAttribute, int *piValue);
|
||||
typedef BOOL (WINAPI * PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC) (HDC hDc, HVIDEOINPUTDEVICENV hDevice);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglBindVideoCaptureDeviceNV (UINT uVideoSlot, HVIDEOINPUTDEVICENV hDevice);
|
||||
UINT WINAPI wglEnumerateVideoCaptureDevicesNV (HDC hDc, HVIDEOINPUTDEVICENV *phDeviceList);
|
||||
BOOL WINAPI wglLockVideoCaptureDeviceNV (HDC hDc, HVIDEOINPUTDEVICENV hDevice);
|
||||
BOOL WINAPI wglQueryVideoCaptureDeviceNV (HDC hDc, HVIDEOINPUTDEVICENV hDevice, int iAttribute, int *piValue);
|
||||
BOOL WINAPI wglReleaseVideoCaptureDeviceNV (HDC hDc, HVIDEOINPUTDEVICENV hDevice);
|
||||
#endif
|
||||
#endif /* WGL_NV_video_capture */
|
||||
|
||||
#ifndef WGL_NV_video_output
|
||||
#define WGL_NV_video_output 1
|
||||
DECLARE_HANDLE(HPVIDEODEV);
|
||||
#define WGL_BIND_TO_VIDEO_RGB_NV 0x20C0
|
||||
#define WGL_BIND_TO_VIDEO_RGBA_NV 0x20C1
|
||||
#define WGL_BIND_TO_VIDEO_RGB_AND_DEPTH_NV 0x20C2
|
||||
#define WGL_VIDEO_OUT_COLOR_NV 0x20C3
|
||||
#define WGL_VIDEO_OUT_ALPHA_NV 0x20C4
|
||||
#define WGL_VIDEO_OUT_DEPTH_NV 0x20C5
|
||||
#define WGL_VIDEO_OUT_COLOR_AND_ALPHA_NV 0x20C6
|
||||
#define WGL_VIDEO_OUT_COLOR_AND_DEPTH_NV 0x20C7
|
||||
#define WGL_VIDEO_OUT_FRAME 0x20C8
|
||||
#define WGL_VIDEO_OUT_FIELD_1 0x20C9
|
||||
#define WGL_VIDEO_OUT_FIELD_2 0x20CA
|
||||
#define WGL_VIDEO_OUT_STACKED_FIELDS_1_2 0x20CB
|
||||
#define WGL_VIDEO_OUT_STACKED_FIELDS_2_1 0x20CC
|
||||
typedef BOOL (WINAPI * PFNWGLGETVIDEODEVICENVPROC) (HDC hDC, int numDevices, HPVIDEODEV *hVideoDevice);
|
||||
typedef BOOL (WINAPI * PFNWGLRELEASEVIDEODEVICENVPROC) (HPVIDEODEV hVideoDevice);
|
||||
typedef BOOL (WINAPI * PFNWGLBINDVIDEOIMAGENVPROC) (HPVIDEODEV hVideoDevice, HPBUFFERARB hPbuffer, int iVideoBuffer);
|
||||
typedef BOOL (WINAPI * PFNWGLRELEASEVIDEOIMAGENVPROC) (HPBUFFERARB hPbuffer, int iVideoBuffer);
|
||||
typedef BOOL (WINAPI * PFNWGLSENDPBUFFERTOVIDEONVPROC) (HPBUFFERARB hPbuffer, int iBufferType, unsigned long *pulCounterPbuffer, BOOL bBlock);
|
||||
typedef BOOL (WINAPI * PFNWGLGETVIDEOINFONVPROC) (HPVIDEODEV hpVideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglGetVideoDeviceNV (HDC hDC, int numDevices, HPVIDEODEV *hVideoDevice);
|
||||
BOOL WINAPI wglReleaseVideoDeviceNV (HPVIDEODEV hVideoDevice);
|
||||
BOOL WINAPI wglBindVideoImageNV (HPVIDEODEV hVideoDevice, HPBUFFERARB hPbuffer, int iVideoBuffer);
|
||||
BOOL WINAPI wglReleaseVideoImageNV (HPBUFFERARB hPbuffer, int iVideoBuffer);
|
||||
BOOL WINAPI wglSendPbufferToVideoNV (HPBUFFERARB hPbuffer, int iBufferType, unsigned long *pulCounterPbuffer, BOOL bBlock);
|
||||
BOOL WINAPI wglGetVideoInfoNV (HPVIDEODEV hpVideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo);
|
||||
#endif
|
||||
#endif /* WGL_NV_video_output */
|
||||
|
||||
#ifndef WGL_OML_sync_control
|
||||
#define WGL_OML_sync_control 1
|
||||
typedef BOOL (WINAPI * PFNWGLGETSYNCVALUESOMLPROC) (HDC hdc, INT64 *ust, INT64 *msc, INT64 *sbc);
|
||||
typedef BOOL (WINAPI * PFNWGLGETMSCRATEOMLPROC) (HDC hdc, INT32 *numerator, INT32 *denominator);
|
||||
typedef INT64 (WINAPI * PFNWGLSWAPBUFFERSMSCOMLPROC) (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder);
|
||||
typedef INT64 (WINAPI * PFNWGLSWAPLAYERBUFFERSMSCOMLPROC) (HDC hdc, int fuPlanes, INT64 target_msc, INT64 divisor, INT64 remainder);
|
||||
typedef BOOL (WINAPI * PFNWGLWAITFORMSCOMLPROC) (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder, INT64 *ust, INT64 *msc, INT64 *sbc);
|
||||
typedef BOOL (WINAPI * PFNWGLWAITFORSBCOMLPROC) (HDC hdc, INT64 target_sbc, INT64 *ust, INT64 *msc, INT64 *sbc);
|
||||
#ifdef WGL_WGLEXT_PROTOTYPES
|
||||
BOOL WINAPI wglGetSyncValuesOML (HDC hdc, INT64 *ust, INT64 *msc, INT64 *sbc);
|
||||
BOOL WINAPI wglGetMscRateOML (HDC hdc, INT32 *numerator, INT32 *denominator);
|
||||
INT64 WINAPI wglSwapBuffersMscOML (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder);
|
||||
INT64 WINAPI wglSwapLayerBuffersMscOML (HDC hdc, int fuPlanes, INT64 target_msc, INT64 divisor, INT64 remainder);
|
||||
BOOL WINAPI wglWaitForMscOML (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder, INT64 *ust, INT64 *msc, INT64 *sbc);
|
||||
BOOL WINAPI wglWaitForSbcOML (HDC hdc, INT64 target_sbc, INT64 *ust, INT64 *msc, INT64 *sbc);
|
||||
#endif
|
||||
#endif /* WGL_OML_sync_control */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,99 +0,0 @@
|
||||
#include "os.h"
|
||||
#include "thread.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __unix__
|
||||
#include <dlfcn.h>
|
||||
|
||||
static mutex dylib_lock;
|
||||
|
||||
dylib* dylib::create(const char * filename, bool * owned)
|
||||
{
|
||||
dylib_lock.lock();
|
||||
dylib* ret=NULL;
|
||||
|
||||
if (owned)
|
||||
{
|
||||
ret=(dylib*)dlopen(filename, RTLD_LAZY|RTLD_NOLOAD);
|
||||
*owned=(!ret);
|
||||
if (ret) return ret;
|
||||
}
|
||||
if (!ret) ret=(dylib*)dlopen(filename, RTLD_LAZY);
|
||||
|
||||
dylib_lock.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* dylib::sym_ptr(const char * name)
|
||||
{
|
||||
return dlsym((void*)this, name);
|
||||
}
|
||||
|
||||
funcptr dylib::sym_func(const char * name)
|
||||
{
|
||||
funcptr ret;
|
||||
*(void**)(&ret)=dlsym((void*)this, name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dylib::release()
|
||||
{
|
||||
dlclose((void*)this);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#undef bind
|
||||
#include <windows.h>
|
||||
#define bind bind_func
|
||||
|
||||
static mutex dylib_lock;
|
||||
|
||||
dylib* dylib::create(const char * filename, bool * owned)
|
||||
{
|
||||
dylib_lock.lock();
|
||||
dylib* ret=NULL;
|
||||
|
||||
if (owned)
|
||||
{
|
||||
if (!GetModuleHandleEx(0, filename, (HMODULE*)&ret)) ret=NULL;
|
||||
*owned=(!ret);
|
||||
//Windows may be able to force load a DLL twice using ntdll!LdrLoadDll
|
||||
// <https://github.com/wine-mirror/wine/blob/master/dlls/ntdll/loader.c#L2324>
|
||||
//but Linux can't, and calling ntdll is generally discouraged, so I'm not using that.
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
//this is so weird dependencies, for example winpthread-1.dll, can be placed beside the dll where they belong
|
||||
char * filename_copy=strdup(filename);
|
||||
char * filename_copy_slash=strrchr(filename_copy, '/');
|
||||
if (!filename_copy_slash) filename_copy_slash=strrchr(filename_copy, '\0');
|
||||
filename_copy_slash[0]='\0';
|
||||
SetDllDirectory(filename_copy);
|
||||
free(filename_copy);
|
||||
|
||||
ret=(dylib*)LoadLibrary(filename);
|
||||
SetDllDirectory(NULL);
|
||||
}
|
||||
|
||||
dylib_lock.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* dylib::sym_ptr(const char * name)
|
||||
{
|
||||
return (void*)GetProcAddress((HMODULE)this, name);
|
||||
}
|
||||
|
||||
funcptr dylib::sym_func(const char * name)
|
||||
{
|
||||
return (funcptr)GetProcAddress((HMODULE)this, name);
|
||||
}
|
||||
|
||||
void dylib::release()
|
||||
{
|
||||
FreeLibrary((HMODULE)this);
|
||||
}
|
||||
#endif
|
||||
@@ -5,9 +5,14 @@
|
||||
|
||||
//This one defines:
|
||||
//Macros END_LITTLE, END_BIG and ENDIAN; ENDIAN is equal to one of the other two. The test is borrowed from byuu's nall.
|
||||
//end_swap() - Byteswaps an integer.
|
||||
//end_nat_to_le(), end_le_to_nat(), end_nat_to_be(), end_be_to_nat() - Byteswaps an integer or returns it unmodified, depending on the host endianness.
|
||||
//Class litend<> and bigend<> - Acts like the given integer type, but is stored by the named endianness internally. Safe to memcpy() and fwrite().
|
||||
//end_swap()
|
||||
// Byteswaps an integer.
|
||||
//end_nat_to_le(), end_le_to_nat(), end_nat_to_be(), end_be_to_nat()
|
||||
// Byteswaps an integer or returns it unmodified, depending on the host endianness.
|
||||
//Class litend<> and bigend<>
|
||||
// Acts like the given integer type, but is stored under the named endianness internally.
|
||||
// Intended to be used for parsing files via struct overlay, or for cross-platform structure passing.
|
||||
// Therefore, it has no padding, and is safe to memcpy() and fwrite().
|
||||
|
||||
#define END_LITTLE 0x04030201
|
||||
#define END_BIG 0x01020304
|
||||
@@ -87,6 +92,9 @@ template<typename T> static inline T end_le_to_nat(T val) { return end_swap(val)
|
||||
template<typename T> static inline T end_be_to_nat(T val) { return val; }
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma pack(push,1)
|
||||
#endif
|
||||
template<typename T, bool little> class endian_core
|
||||
{
|
||||
T val;
|
||||
@@ -103,17 +111,21 @@ public:
|
||||
if (little == (ENDIAN==END_LITTLE)) val = newval;
|
||||
else val = end_swap(newval);
|
||||
}
|
||||
};
|
||||
}
|
||||
#ifdef __GNUC__
|
||||
__attribute__((__packed__))
|
||||
#endif
|
||||
;
|
||||
#ifdef _MSC_VER
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
//This one doesn't optimize properly. While it does get unrolled, it remains as four byte loads, and some shift/or.
|
||||
template<typename T, bool little> class endian_core
|
||||
{
|
||||
union {
|
||||
T align;
|
||||
uint8_t bytes[sizeof(T)];
|
||||
};
|
||||
uint8_t bytes[sizeof(T)];
|
||||
|
||||
public:
|
||||
operator T()
|
||||
|
||||
55
arlib/file-mem.cpp
Normal file
55
arlib/file-mem.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "file.h"
|
||||
#include "os.h"
|
||||
|
||||
namespace {
|
||||
class file_mem : public filewrite {
|
||||
public:
|
||||
array<byte> datawr;
|
||||
arrayview<byte> datard;
|
||||
|
||||
file_mem(arrayview<byte> data) : filewrite("", data.size()), datard(data) {}
|
||||
file_mem(array<byte> data) : filewrite("", data.size()), datawr(data), datard(datawr) {}
|
||||
|
||||
size_t read(arrayvieww<byte> target, size_t start)
|
||||
{
|
||||
size_t bytes_dst = target.size();
|
||||
size_t bytes_src = datard.size()-start;
|
||||
size_t bytes = min(bytes_dst, bytes_src);
|
||||
memcpy(target.ptr(), datard.ptr()+start, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
arrayview<byte> mmap(size_t start, size_t len) { return datard.slice(start, len); }
|
||||
void unmap(arrayview<byte> data) {}
|
||||
|
||||
bool resize(size_t newsize)
|
||||
{
|
||||
datawr.resize(newsize);
|
||||
datard = datawr;
|
||||
len = newsize;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool write(arrayview<byte> data, size_t start)
|
||||
{
|
||||
size_t bytes_src = data.size();
|
||||
size_t bytes_dst = datawr.size()-start;
|
||||
size_t bytes = min(bytes_dst, bytes_src);
|
||||
memcpy(datawr.ptr()+start, data.ptr(), bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
arrayvieww<byte> mmapw(size_t start, size_t len) { return datawr.slice(start, len); }
|
||||
void unmapw(arrayvieww<byte> data) {}
|
||||
};
|
||||
}
|
||||
|
||||
file* file::create_mem_view(arrayview<byte> data)
|
||||
{
|
||||
return new file_mem(data);
|
||||
}
|
||||
|
||||
filewrite* file::create_mem_copy(array<byte> data)
|
||||
{
|
||||
return new file_mem(data);
|
||||
}
|
||||
@@ -1,302 +0,0 @@
|
||||
#include "file.h"
|
||||
#include "os.h"
|
||||
#include "thread.h"
|
||||
|
||||
#define MMAP_THRESHOLD 128*1024
|
||||
|
||||
#ifdef __unix__
|
||||
#include <unistd.h>
|
||||
//#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
//other platforms: http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe
|
||||
const char * window_get_proc_path()
|
||||
{
|
||||
//we could lstat it, but apparently that just returns zero on /proc on Linux.
|
||||
|
||||
ssize_t bufsize=64;
|
||||
static char * linkname=NULL;
|
||||
if (linkname) return linkname;
|
||||
|
||||
while (true)
|
||||
{
|
||||
linkname=malloc(bufsize);
|
||||
ssize_t r=readlink("/proc/self/exe", linkname, bufsize);
|
||||
if (r<0 || r>=bufsize)
|
||||
{
|
||||
free(linkname);
|
||||
if (r<0) return NULL;
|
||||
|
||||
bufsize*=2;
|
||||
continue;
|
||||
}
|
||||
linkname[r]='\0';
|
||||
char * end=strrchr(linkname, '/');
|
||||
if (end) *end='\0';
|
||||
|
||||
return linkname;
|
||||
}
|
||||
}
|
||||
|
||||
static void window_cwd_enter(const char * dir);
|
||||
static void window_cwd_leave();
|
||||
|
||||
char * _window_native_get_absolute_path(const char * basepath, const char * path, bool allow_up)
|
||||
{
|
||||
if (!basepath || !path) return NULL;
|
||||
const char * filepart=strrchr(basepath, '/');
|
||||
if (!filepart) return NULL;
|
||||
char * basedir=strndup(basepath, filepart+1-basepath);
|
||||
|
||||
window_cwd_enter(basedir);
|
||||
char * ret=realpath(path, NULL);
|
||||
window_cwd_leave();
|
||||
|
||||
if (!allow_up && ret && strncasecmp(basedir, ret, filepart+1-basepath)!=0)
|
||||
{
|
||||
free(ret);
|
||||
ret=NULL;
|
||||
}
|
||||
free(basedir);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char * cwd_init;
|
||||
static const char * cwd_bogus;
|
||||
static mutex cwd_mutex;
|
||||
|
||||
static void window_cwd_enter(const char * dir)
|
||||
{
|
||||
cwd_mutex.lock();
|
||||
char * cwd_bogus_check=getcwd(NULL, 0);
|
||||
if (strcmp(cwd_bogus, cwd_bogus_check)!=0) abort();//if this fires, someone changed the directory without us knowing - not allowed. cwd belongs to the frontend.
|
||||
free(cwd_bogus_check);
|
||||
ignore(chdir(dir));
|
||||
}
|
||||
|
||||
static void window_cwd_leave()
|
||||
{
|
||||
ignore(chdir(cwd_bogus));
|
||||
cwd_mutex.unlock();
|
||||
}
|
||||
|
||||
const char * window_get_cwd()
|
||||
{
|
||||
return cwd_init;
|
||||
}
|
||||
|
||||
void _window_init_file()
|
||||
{
|
||||
char * cwd_init_tmp=getcwd(NULL, 0);
|
||||
char * cwdend=strrchr(cwd_init_tmp, '/');
|
||||
if (!cwdend) cwd_init="/";
|
||||
else if (cwdend[1]=='/') cwd_init=cwd_init_tmp;
|
||||
else
|
||||
{
|
||||
size_t cwdlen=strlen(cwd_init_tmp);
|
||||
char * cwd_init_fixed=malloc(cwdlen+1+1);
|
||||
memcpy(cwd_init_fixed, cwd_init_tmp, cwdlen);
|
||||
cwd_init_fixed[cwdlen+0]='/';
|
||||
cwd_init_fixed[cwdlen+1]='\0';
|
||||
cwd_init=cwd_init_fixed;
|
||||
free(cwd_init_tmp);
|
||||
}
|
||||
|
||||
//try a couple of useless directories and hope one of them works
|
||||
//this seems to be the best one:
|
||||
//- even root can't create files here
|
||||
//- it contains no files with a plausible name on a standard Ubuntu box (I have an ath9k-phy0, nothing will ever want that filename)
|
||||
//- a wild write will not do anything dangerous except turn on some lamps
|
||||
!chdir("/sys/class/leds/") ||
|
||||
//the rest are in case it's not accessible (weird chroot? not linux?), so try some random things
|
||||
!chdir("/sys/") ||
|
||||
!chdir("/dev/") ||
|
||||
!chdir("/home/") ||
|
||||
!chdir("/tmp/") ||
|
||||
!chdir("/");
|
||||
cwd_bogus = getcwd(NULL, 0);//POSIX does not specify getcwd(NULL), it's Linux-specific
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
static void* file_alloc(int fd, size_t len, bool writable)
|
||||
{
|
||||
if (len <= MMAP_THRESHOLD)
|
||||
{
|
||||
uint8_t* data=malloc(len+1);
|
||||
pread(fd, data, len, 0);
|
||||
data[len]='\0';
|
||||
return data;
|
||||
}
|
||||
else
|
||||
{
|
||||
void* data=mmap(NULL, len+1, writable ? (PROT_READ|PROT_WRITE) : PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (data==MAP_FAILED) return NULL;
|
||||
|
||||
if (len % sysconf(_SC_PAGESIZE) == 0)
|
||||
{
|
||||
mmap((char*)data + len, 1, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
static long pagesize() { return sysconf(_SC_PAGESIZE); }
|
||||
|
||||
namespace {
|
||||
class file_fs : public file {
|
||||
int fd;
|
||||
public:
|
||||
//do not use the file pointer, dup() doesn't clone that one
|
||||
file_fs(const char * filename, int fd, size_t len) : file(filename) { this->fd=fd; this->len=len; }
|
||||
|
||||
file* clone() { return new file_fs(this->filename, dup(this->fd), this->len); }
|
||||
|
||||
size_t read(void* target, size_t start, size_t len)
|
||||
{
|
||||
ssize_t ret = pread(fd, target, len, start);
|
||||
if (ret < 0) return 0;
|
||||
else return ret;
|
||||
}
|
||||
|
||||
void* mmap(size_t start, size_t len)
|
||||
{
|
||||
size_t offset = start % pagesize();
|
||||
void* data=::mmap(NULL, len+offset, PROT_READ, MAP_SHARED, this->fd, start-offset);
|
||||
if (data==MAP_FAILED) return NULL;
|
||||
return (char*)data+offset;
|
||||
}
|
||||
|
||||
void unmap(const void* data, size_t len)
|
||||
{
|
||||
size_t offset = (uintptr_t)data % pagesize();
|
||||
munmap((char*)data-offset, len+offset);
|
||||
}
|
||||
~file_fs() { close(fd); }
|
||||
};
|
||||
}
|
||||
|
||||
file* file::create_fs(const char * filename)
|
||||
{
|
||||
int fd=open(filename, O_RDONLY);
|
||||
if (fd<0) return NULL;
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st)<0) goto fail;
|
||||
|
||||
return new file_fs(filename, fd, st.st_size);
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef ARGUI_NONE
|
||||
file* file::create(const char * filename)
|
||||
{
|
||||
return create_fs(filename);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
namespace {
|
||||
class file_fs_wr : public filewrite {
|
||||
public:
|
||||
int fd;
|
||||
bool truncate;
|
||||
file_fs_wr(int fd) : fd(fd) {}
|
||||
|
||||
/*private*/ void alloc(size_t size)
|
||||
{
|
||||
this->data=file_alloc(this->fd, size, true);
|
||||
this->len=size;
|
||||
if (this->data==NULL) abort();
|
||||
}
|
||||
|
||||
/*private*/ void dealloc()
|
||||
{
|
||||
//no msync - munmap is guaranteed to do that already (and linux tracks dirty pages anyways)
|
||||
if (this->len <= MMAP_THRESHOLD)
|
||||
{
|
||||
pwrite(this->fd, this->data, this->len, 0);
|
||||
free(this->data);
|
||||
}
|
||||
else
|
||||
{
|
||||
munmap(this->data, this->len+1);
|
||||
}
|
||||
}
|
||||
|
||||
bool resize(size_t newsize)
|
||||
{
|
||||
if (ftruncate(this->fd, newsize) < 0) return false;
|
||||
if (this->len < MMAP_THRESHOLD && newsize < MMAP_THRESHOLD)
|
||||
{
|
||||
this->len=newsize;
|
||||
uint8_t* data=realloc(this->data, newsize+1);
|
||||
data[newsize]='\0';
|
||||
this->data=data;
|
||||
return true;
|
||||
}
|
||||
dealloc();
|
||||
alloc(newsize);
|
||||
return true;
|
||||
}
|
||||
|
||||
void sync()
|
||||
{
|
||||
if (this->truncate)
|
||||
{
|
||||
ftruncate(this->fd, this->len);
|
||||
this->truncate=false;
|
||||
}
|
||||
msync(this->data, this->len, MS_SYNC);//no MS_INVALIDATE because I can't figure out what it's supposed to do
|
||||
//on linux, it does nothing whatsoever, except in some EINVAL handlers
|
||||
}
|
||||
|
||||
~file_fs_wr()
|
||||
{
|
||||
sync();
|
||||
dealloc();
|
||||
close(this->fd);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
filewrite* filewrite::create_fs(const char * filename, bool truncate)
|
||||
{
|
||||
static const int oflags[]={ O_RDWR|O_CREAT, O_WRONLY|O_CREAT };
|
||||
int fd=open(filename, oflags[truncate], 0666);//umask defaults to turning this to 644
|
||||
if (fd<0) return NULL;
|
||||
|
||||
if (truncate)
|
||||
{
|
||||
file_fs_wr* f=new file_fs_wr(fd);
|
||||
f->truncate=true;
|
||||
return f;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct stat st;
|
||||
if (fstat(fd, &st)<0) goto fail;
|
||||
|
||||
file_fs_wr* f; f=new file_fs_wr(fd);
|
||||
f->alloc(st.st_size);
|
||||
return f;
|
||||
}
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
126
arlib/file-test.cpp
Normal file
126
arlib/file-test.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
#include "file.h"
|
||||
#include "test.h"
|
||||
|
||||
#ifdef ARLIB_TEST
|
||||
|
||||
//criteria:
|
||||
//- must be a normal file, no /dev/*
|
||||
//- minimum 66000 bytes
|
||||
//- the first few bytes must be known, no .txt files or possibly-shebanged stuff
|
||||
//- the file must contain somewhat unpredictable data, nothing from /dev/zero
|
||||
//- must be readable by everyone (assuming absense of sandboxes)
|
||||
//- must NOT be writable or deletable by this program
|
||||
//recommended choice: some random executable
|
||||
#ifdef _WIN32
|
||||
#define READONLY_FILE "C:/Windows/notepad.exe" // screw anything where the windows directory isn't on C:
|
||||
#define READONLY_FILE_HEAD "MZ"
|
||||
#else
|
||||
#define READONLY_FILE "/bin/sh"
|
||||
#define READONLY_FILE_HEAD "\x7F""ELF"
|
||||
#endif
|
||||
|
||||
//criteria:
|
||||
//- no funny symbols
|
||||
//- implausible name, nothing of value must be lost by deleting it
|
||||
#define WRITABLE_FILE "arlib-selftest.txt"
|
||||
|
||||
test("file reading")
|
||||
{
|
||||
autoptr<file> f = file::open(READONLY_FILE);
|
||||
assert(f);
|
||||
assert(f->len);
|
||||
assert(f->len > strlen(READONLY_FILE_HEAD));
|
||||
assert(f->len >= 66000);
|
||||
array<byte> bytes = f->read();
|
||||
assert(bytes.size() == f->len);
|
||||
assert(!memcmp(bytes.ptr(), READONLY_FILE_HEAD, strlen(READONLY_FILE_HEAD)));
|
||||
|
||||
arrayview<byte> map = f->mmap();
|
||||
assert(map.ptr());
|
||||
assert(map.size() == f->len);
|
||||
assert(!memcmp(bytes.ptr(), map.ptr(), bytes.size()));
|
||||
|
||||
arrayview<byte> map2 = f->mmap();
|
||||
assert(map2.ptr());
|
||||
assert(map2.size() == f->len);
|
||||
assert(!memcmp(bytes.ptr(), map2.ptr(), bytes.size()));
|
||||
f->unmap(map2);
|
||||
|
||||
const size_t t_start[] = { 0, 65536, 4096, 1, 1, 1, 65537, 65535 };
|
||||
const size_t t_len[] = { 66000, 400, 400, 65535, 65536, 65999, 400, 2 };
|
||||
for (size_t i=0;i<ARRAY_SIZE(t_start);i++)
|
||||
{
|
||||
arrayview<byte> map3 = f->mmap(t_start[i], t_len[i]);
|
||||
assert(map3.ptr());
|
||||
assert(map3.size() == t_len[i]);
|
||||
assert(!memcmp(bytes.ptr()+t_start[i], map3.ptr(), t_len[i]));
|
||||
f->unmap(map3);
|
||||
}
|
||||
|
||||
f->unmap(map);
|
||||
}
|
||||
|
||||
test("file writing")
|
||||
{
|
||||
autoptr<filewrite> f;
|
||||
|
||||
assert(!filewrite::open(READONLY_FILE, filewrite::m_default));
|
||||
assert(!filewrite::open(READONLY_FILE, filewrite::m_existing));
|
||||
assert(!filewrite::open(READONLY_FILE, filewrite::m_replace));
|
||||
assert(!filewrite::open(READONLY_FILE, filewrite::m_create_excl));
|
||||
|
||||
assert(filewrite::unlink(WRITABLE_FILE));
|
||||
|
||||
assert(!file::open(WRITABLE_FILE));
|
||||
|
||||
f = filewrite::open(WRITABLE_FILE);
|
||||
assert(f);
|
||||
assert(f->replace("foo"));
|
||||
|
||||
assert_eq(string(file::read(WRITABLE_FILE)), "foo");
|
||||
|
||||
f->resize(8);
|
||||
assert(f->len == 8);
|
||||
byte expected[8]={'f','o','o',0,0,0,0,0};
|
||||
array<byte> actual = file::read(WRITABLE_FILE);
|
||||
assert(actual.ptr());
|
||||
assert(actual.size()==8);
|
||||
assert(!memcmp(actual.ptr(), expected, 8));
|
||||
|
||||
arrayvieww<byte> map = f->mmapw();
|
||||
assert(map.ptr());
|
||||
assert_eq(map.size(), 8);
|
||||
assert(!memcmp(map.ptr(), expected, 8));
|
||||
map[3]='t';
|
||||
f->unmapw(map);
|
||||
|
||||
expected[3] = 't';
|
||||
actual = file::read(WRITABLE_FILE);
|
||||
assert(actual.ptr());
|
||||
assert(actual.size()==8);
|
||||
assert(!memcmp(actual.ptr(), expected, 8));
|
||||
|
||||
f = NULL;
|
||||
|
||||
//test the various creation modes
|
||||
//file exists, these three should work
|
||||
f=NULL; assert( (f=filewrite::open(WRITABLE_FILE, filewrite::m_default)));
|
||||
f=NULL; assert( (f=filewrite::open(WRITABLE_FILE, filewrite::m_existing)));
|
||||
assert_eq(f->len, 8);
|
||||
f=NULL; assert( (f=filewrite::open(WRITABLE_FILE, filewrite::m_replace)));
|
||||
assert_eq(f->len, 0);
|
||||
f=NULL; assert(!(f=filewrite::open(WRITABLE_FILE, filewrite::m_create_excl)));//but this shouldn't
|
||||
|
||||
f=NULL;
|
||||
assert(filewrite::unlink(WRITABLE_FILE));
|
||||
assert(!filewrite::open(WRITABLE_FILE, filewrite::m_existing)); // this should fail
|
||||
f=NULL; assert(f=filewrite::open(WRITABLE_FILE, filewrite::m_create_excl)); // this should create
|
||||
assert(filewrite::unlink(WRITABLE_FILE));
|
||||
|
||||
f=NULL; assert(f=filewrite::open(WRITABLE_FILE, filewrite::m_replace)); // replacing a nonexistent file is fine
|
||||
//opening a nonexistent file with m_default is tested at the start of this function
|
||||
f=NULL;
|
||||
assert(filewrite::unlink(WRITABLE_FILE));
|
||||
assert(filewrite::unlink(WRITABLE_FILE)); // ensure it properly deals with unlinking a nonexistent file
|
||||
}
|
||||
#endif
|
||||
226
arlib/file-unix.cpp
Normal file
226
arlib/file-unix.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
#include "file.h"
|
||||
#include "os.h"
|
||||
#include "thread.h"
|
||||
|
||||
#ifdef __unix__
|
||||
#include <unistd.h>
|
||||
//#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <errno.h>
|
||||
|
||||
//#define MMAP_THRESHOLD 128*1024
|
||||
//
|
||||
////other platforms: http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe
|
||||
//const char * window_get_proc_path()
|
||||
//{
|
||||
// //we could lstat it, but apparently that just returns zero on /proc on Linux.
|
||||
//
|
||||
// ssize_t bufsize=64;
|
||||
// static char * linkname=NULL;
|
||||
// if (linkname) return linkname;
|
||||
//
|
||||
// while (true)
|
||||
// {
|
||||
// linkname=malloc(bufsize);
|
||||
// ssize_t r=readlink("/proc/self/exe", linkname, bufsize);
|
||||
// if (r<0 || r>=bufsize)
|
||||
// {
|
||||
// free(linkname);
|
||||
// if (r<0) return NULL;
|
||||
//
|
||||
// bufsize*=2;
|
||||
// continue;
|
||||
// }
|
||||
// linkname[r]='\0';
|
||||
// char * end=strrchr(linkname, '/');
|
||||
// if (end) *end='\0';
|
||||
//
|
||||
// return linkname;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//static void window_cwd_enter(const char * dir);
|
||||
//static void window_cwd_leave();
|
||||
//
|
||||
//char * _window_native_get_absolute_path(const char * basepath, const char * path, bool allow_up)
|
||||
//{
|
||||
// if (!basepath || !path) return NULL;
|
||||
// const char * filepart=strrchr(basepath, '/');
|
||||
// if (!filepart) return NULL;
|
||||
// char * basedir=strndup(basepath, filepart+1-basepath);
|
||||
//
|
||||
// window_cwd_enter(basedir);
|
||||
// char * ret=realpath(path, NULL);
|
||||
// window_cwd_leave();
|
||||
//
|
||||
// if (!allow_up && ret && strncasecmp(basedir, ret, filepart+1-basepath)!=0)
|
||||
// {
|
||||
// free(ret);
|
||||
// ret=NULL;
|
||||
// }
|
||||
// free(basedir);
|
||||
//
|
||||
// return ret;
|
||||
//}
|
||||
//
|
||||
//static const char * cwd_init;
|
||||
//static const char * cwd_bogus;
|
||||
//static mutex cwd_mutex;
|
||||
//
|
||||
//static void window_cwd_enter(const char * dir)
|
||||
//{
|
||||
// cwd_mutex.lock();
|
||||
// char * cwd_bogus_check=getcwd(NULL, 0);
|
||||
// if (strcmp(cwd_bogus, cwd_bogus_check)!=0) abort();//if this fires, someone changed the directory without us knowing - not allowed. cwd belongs to the frontend.
|
||||
// free(cwd_bogus_check);
|
||||
// ignore(chdir(dir));
|
||||
//}
|
||||
//
|
||||
//static void window_cwd_leave()
|
||||
//{
|
||||
// ignore(chdir(cwd_bogus));
|
||||
// cwd_mutex.unlock();
|
||||
//}
|
||||
//
|
||||
//const char * window_get_cwd()
|
||||
//{
|
||||
// return cwd_init;
|
||||
//}
|
||||
//
|
||||
//void _window_init_file()
|
||||
//{
|
||||
// char * cwd_init_tmp=getcwd(NULL, 0);
|
||||
// char * cwdend=strrchr(cwd_init_tmp, '/');
|
||||
// if (!cwdend) cwd_init="/";
|
||||
// else if (cwdend[1]=='/') cwd_init=cwd_init_tmp;
|
||||
// else
|
||||
// {
|
||||
// size_t cwdlen=strlen(cwd_init_tmp);
|
||||
// char * cwd_init_fixed=malloc(cwdlen+1+1);
|
||||
// memcpy(cwd_init_fixed, cwd_init_tmp, cwdlen);
|
||||
// cwd_init_fixed[cwdlen+0]='/';
|
||||
// cwd_init_fixed[cwdlen+1]='\0';
|
||||
// cwd_init=cwd_init_fixed;
|
||||
// free(cwd_init_tmp);
|
||||
// }
|
||||
//
|
||||
// //try a couple of useless directories and hope one of them works
|
||||
// //this seems to be the best one:
|
||||
// //- even root can't create files here
|
||||
// //- it contains no files with a plausible name on a standard Ubuntu box (I have an ath9k-phy0, nothing will ever want that filename)
|
||||
// //- a wild write will not do anything dangerous except turn on some lamps
|
||||
// !chdir("/sys/class/leds/") ||
|
||||
// //the rest are in case it's not accessible (weird chroot? not linux?), so try some random things
|
||||
// !chdir("/sys/") ||
|
||||
// !chdir("/dev/") ||
|
||||
// !chdir("/home/") ||
|
||||
// !chdir("/tmp/") ||
|
||||
// !chdir("/");
|
||||
// cwd_bogus = getcwd(NULL, 0);//POSIX does not specify getcwd(NULL), it's Linux-specific
|
||||
//}
|
||||
|
||||
|
||||
static long pagesize;
|
||||
|
||||
namespace {
|
||||
class file_fs : public filewrite {
|
||||
public:
|
||||
int fd;
|
||||
|
||||
file_fs(cstring filename, int fd) : filewrite(filename), fd(fd)
|
||||
{
|
||||
len = lseek(fd, 0, SEEK_END);
|
||||
}
|
||||
|
||||
size_t read(arrayvieww<byte> target, size_t start)
|
||||
{
|
||||
size_t ret = pread(fd, target.ptr(), target.size(), start);
|
||||
if (ret<0) return 0;
|
||||
else return ret;
|
||||
}
|
||||
|
||||
bool resize(size_t newsize)
|
||||
{
|
||||
bool ret = (ftruncate(this->fd, newsize)==0);
|
||||
len = lseek(fd, 0, SEEK_END);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool write(arrayview<byte> data, size_t start)
|
||||
{
|
||||
size_t ret = pwrite(fd, data.ptr(), data.size(), start);
|
||||
if (ret<0) return 0;
|
||||
else return ret;
|
||||
}
|
||||
|
||||
/*private*/ arrayvieww<byte> mmap(bool write, size_t start, size_t len)
|
||||
{
|
||||
size_t offset = start % pagesize;
|
||||
void* data=::mmap(NULL, len+offset, write ? PROT_WRITE|PROT_READ : PROT_READ, MAP_SHARED, this->fd, start-offset);
|
||||
if (data==MAP_FAILED) return NULL;
|
||||
return arrayvieww<byte>((uint8_t*)data+offset, len);
|
||||
}
|
||||
|
||||
arrayview<byte> mmap(size_t start, size_t len) { return mmap(false, start, len); }
|
||||
void unmap(arrayview<byte> data)
|
||||
{
|
||||
size_t offset = (uintptr_t)data.ptr() % pagesize;
|
||||
munmap((char*)data.ptr()-offset, data.size()+offset);
|
||||
}
|
||||
|
||||
arrayvieww<byte> mmapw(size_t start, size_t len) { return mmap(true, start, len); }
|
||||
void unmapw(arrayvieww<byte> data) { unmap(data); }
|
||||
|
||||
~file_fs() { close(fd); }
|
||||
};
|
||||
}
|
||||
|
||||
file* file::open_fs(cstring filename)
|
||||
{
|
||||
int fd = ::open(filename, O_RDONLY);
|
||||
if (fd<0) return NULL;
|
||||
return new file_fs(filename, fd);
|
||||
}
|
||||
|
||||
filewrite* filewrite::open_fs(cstring filename, mode m)
|
||||
{
|
||||
int flags[] = { O_RDWR|O_CREAT, O_RDWR, O_RDWR|O_CREAT|O_TRUNC, O_RDWR|O_CREAT|O_EXCL };
|
||||
int fd = ::open(filename, flags[m], 0666);
|
||||
if (fd<0) return NULL;
|
||||
return new file_fs(filename, fd);
|
||||
}
|
||||
|
||||
bool filewrite::unlink_fs(cstring filename)
|
||||
{
|
||||
int ret = ::unlink(filename);
|
||||
return ret==0 || (ret==-1 && errno==ENOENT);
|
||||
}
|
||||
|
||||
//#ifdef ARGUI_NONE
|
||||
file* file::open(cstring filename)
|
||||
{
|
||||
return open_fs(filename);
|
||||
}
|
||||
|
||||
filewrite* filewrite::open(cstring filename, mode m)
|
||||
{
|
||||
return open_fs(filename, m);
|
||||
}
|
||||
|
||||
bool filewrite::unlink(cstring filename)
|
||||
{
|
||||
return unlink_fs(filename);
|
||||
}
|
||||
//#endif
|
||||
|
||||
void _window_init_file()
|
||||
{
|
||||
pagesize = sysconf(_SC_PAGESIZE);
|
||||
}
|
||||
#endif
|
||||
@@ -1,8 +1,5 @@
|
||||
#include "file.h"
|
||||
#include "os.h"
|
||||
#include "thread.h"
|
||||
|
||||
#define MMAP_THRESHOLD 128*1024
|
||||
|
||||
#ifdef _WIN32
|
||||
#undef bind
|
||||
@@ -10,122 +7,122 @@
|
||||
#define bind bind_func
|
||||
#include <string.h>
|
||||
|
||||
static void window_cwd_enter(const char * dir);
|
||||
static void window_cwd_leave();
|
||||
//#define MMAP_THRESHOLD 32*1024
|
||||
|
||||
//other platforms: http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe
|
||||
const char * window_get_proc_path()
|
||||
{
|
||||
//TODO: not thread safe
|
||||
static char path[MAX_PATH];
|
||||
GetModuleFileName(NULL, path, MAX_PATH);
|
||||
for (int i=0;path[i];i++)
|
||||
{
|
||||
if (path[i]=='\\') path[i]='/';
|
||||
}
|
||||
char * end=strrchr(path, '/');
|
||||
if (end) end[1]='\0';
|
||||
return path;
|
||||
}
|
||||
////other platforms: http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe
|
||||
//const char * window_get_proc_path()
|
||||
//{
|
||||
// //TODO: not thread safe
|
||||
// static char path[MAX_PATH];
|
||||
// GetModuleFileName(NULL, path, MAX_PATH);
|
||||
// for (int i=0;path[i];i++)
|
||||
// {
|
||||
// if (path[i]=='\\') path[i]='/';
|
||||
// }
|
||||
// char * end=strrchr(path, '/');
|
||||
// if (end) end[1]='\0';
|
||||
// return path;
|
||||
//}
|
||||
|
||||
char * _window_native_get_absolute_path(const char * basepath, const char * path, bool allow_up)
|
||||
{
|
||||
if (!path || !basepath) return NULL;
|
||||
|
||||
DWORD len=GetFullPathName(basepath, 0, NULL, NULL);
|
||||
char * matchdir=malloc(len);
|
||||
char * filepart;
|
||||
GetFullPathName(basepath, len, matchdir, &filepart);
|
||||
if (filepart) *filepart='\0';
|
||||
window_cwd_enter(matchdir);
|
||||
for (unsigned int i=0;matchdir[i];i++)
|
||||
{
|
||||
if (matchdir[i]=='\\') matchdir[i]='/';
|
||||
}
|
||||
|
||||
len=GetFullPathName(path, 0, NULL, NULL);
|
||||
char * ret=malloc(len);
|
||||
GetFullPathName(path, len, ret, NULL);
|
||||
|
||||
window_cwd_leave();
|
||||
|
||||
for (unsigned int i=0;i<len;i++)
|
||||
{
|
||||
if (ret[i]=='\\') ret[i]='/';
|
||||
}
|
||||
|
||||
if (!allow_up)
|
||||
{
|
||||
if (strncasecmp(matchdir, ret, strlen(matchdir))!=0)
|
||||
{
|
||||
free(matchdir);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
free(matchdir);
|
||||
|
||||
return ret;
|
||||
}
|
||||
//char * _window_native_get_absolute_path(const char * basepath, const char * path, bool allow_up)
|
||||
//{
|
||||
// if (!path || !basepath) return NULL;
|
||||
//
|
||||
// DWORD len=GetFullPathName(basepath, 0, NULL, NULL);
|
||||
// char * matchdir=malloc(len);
|
||||
// char * filepart;
|
||||
// GetFullPathName(basepath, len, matchdir, &filepart);
|
||||
// if (filepart) *filepart='\0';
|
||||
// window_cwd_enter(matchdir);
|
||||
// for (unsigned int i=0;matchdir[i];i++)
|
||||
// {
|
||||
// if (matchdir[i]=='\\') matchdir[i]='/';
|
||||
// }
|
||||
//
|
||||
// len=GetFullPathName(path, 0, NULL, NULL);
|
||||
// char * ret=malloc(len);
|
||||
// GetFullPathName(path, len, ret, NULL);
|
||||
//
|
||||
// window_cwd_leave();
|
||||
//
|
||||
// for (unsigned int i=0;i<len;i++)
|
||||
// {
|
||||
// if (ret[i]=='\\') ret[i]='/';
|
||||
// }
|
||||
//
|
||||
// if (!allow_up)
|
||||
// {
|
||||
// if (strncasecmp(matchdir, ret, strlen(matchdir))!=0)
|
||||
// {
|
||||
// free(matchdir);
|
||||
// free(ret);
|
||||
// return NULL;
|
||||
// }
|
||||
// }
|
||||
// free(matchdir);
|
||||
//
|
||||
// return ret;
|
||||
//}
|
||||
|
||||
static char * cwd_init;
|
||||
static char * cwd_bogus;
|
||||
static char * cwd_bogus_check;
|
||||
static DWORD cwd_bogus_check_len;
|
||||
static mutex cwd_lock;
|
||||
//static char * cwd_init;
|
||||
//static char * cwd_bogus;
|
||||
//static char * cwd_bogus_check;
|
||||
//static DWORD cwd_bogus_check_len;
|
||||
//static mutex cwd_lock;
|
||||
//
|
||||
//static void window_cwd_enter(const char * dir)
|
||||
//{
|
||||
// cwd_lock.lock();
|
||||
// GetCurrentDirectory(cwd_bogus_check_len, cwd_bogus_check);
|
||||
// //if this fires, someone changed the directory without us knowing - not allowed. cwd belongs to the frontend.
|
||||
// if (strcmp(cwd_bogus, cwd_bogus_check)!=0) abort();
|
||||
// SetCurrentDirectory(dir);
|
||||
//}
|
||||
//
|
||||
//static void window_cwd_leave()
|
||||
//{
|
||||
// SetCurrentDirectory(cwd_bogus);
|
||||
// cwd_lock.unlock();
|
||||
//}
|
||||
//
|
||||
//const char * window_get_cwd()
|
||||
//{
|
||||
// return cwd_init;
|
||||
//}
|
||||
|
||||
static void window_cwd_enter(const char * dir)
|
||||
{
|
||||
cwd_lock.lock();
|
||||
GetCurrentDirectory(cwd_bogus_check_len, cwd_bogus_check);
|
||||
if (strcmp(cwd_bogus, cwd_bogus_check)!=0) abort();//if this fires, someone changed the directory without us knowing - not allowed. cwd belongs to the frontend.
|
||||
SetCurrentDirectory(dir);
|
||||
}
|
||||
|
||||
static void window_cwd_leave()
|
||||
{
|
||||
SetCurrentDirectory(cwd_bogus);
|
||||
cwd_lock.unlock();
|
||||
}
|
||||
|
||||
const char * window_get_cwd()
|
||||
{
|
||||
return cwd_init;
|
||||
}
|
||||
|
||||
void _window_init_file()
|
||||
{
|
||||
DWORD len=GetCurrentDirectory(0, NULL);
|
||||
cwd_init=malloc(len+1);
|
||||
GetCurrentDirectory(len, cwd_init);
|
||||
len=strlen(cwd_init);
|
||||
for (unsigned int i=0;i<len;i++)
|
||||
{
|
||||
if (cwd_init[i]=='\\') cwd_init[i]='/';
|
||||
}
|
||||
if (cwd_init[len-1]!='/')
|
||||
{
|
||||
cwd_init[len+0]='/';
|
||||
cwd_init[len+1]='\0';
|
||||
}
|
||||
|
||||
//try a couple of useless directories and hope one of them works
|
||||
//(this code is downright Perl-like, but the alternative is a pile of ugly nesting)
|
||||
SetCurrentDirectory("\\Users") ||
|
||||
SetCurrentDirectory("\\Documents and Settings") ||
|
||||
SetCurrentDirectory("\\Windows") ||
|
||||
(SetCurrentDirectory("C:\\") && false) ||
|
||||
SetCurrentDirectory("\\Users") ||
|
||||
SetCurrentDirectory("\\Documents and Settings") ||
|
||||
SetCurrentDirectory("\\Windows") ||
|
||||
SetCurrentDirectory("\\");
|
||||
|
||||
len=GetCurrentDirectory(0, NULL);
|
||||
cwd_bogus=malloc(len);
|
||||
cwd_bogus_check=malloc(len);
|
||||
cwd_bogus_check_len=len;
|
||||
GetCurrentDirectory(len, cwd_bogus);
|
||||
}
|
||||
//void _window_init_file()
|
||||
//{
|
||||
// DWORD len=GetCurrentDirectory(0, NULL);
|
||||
// cwd_init=malloc(len+1);
|
||||
// GetCurrentDirectory(len, cwd_init);
|
||||
// len=strlen(cwd_init);
|
||||
// for (unsigned int i=0;i<len;i++)
|
||||
// {
|
||||
// if (cwd_init[i]=='\\') cwd_init[i]='/';
|
||||
// }
|
||||
// if (cwd_init[len-1]!='/')
|
||||
// {
|
||||
// cwd_init[len+0]='/';
|
||||
// cwd_init[len+1]='\0';
|
||||
// }
|
||||
//
|
||||
// //try a couple of useless directories and hope one of them works
|
||||
// //(this code is downright Perl-like, but the alternative is a pile of ugly nesting)
|
||||
// SetCurrentDirectory("\\Users") ||
|
||||
// SetCurrentDirectory("\\Documents and Settings") ||
|
||||
// SetCurrentDirectory("\\Windows") ||
|
||||
// (SetCurrentDirectory("C:\\") && false) ||
|
||||
// SetCurrentDirectory("\\Users") ||
|
||||
// SetCurrentDirectory("\\Documents and Settings") ||
|
||||
// SetCurrentDirectory("\\Windows") ||
|
||||
// SetCurrentDirectory("\\");
|
||||
//
|
||||
// len=GetCurrentDirectory(0, NULL);
|
||||
// cwd_bogus=malloc(len);
|
||||
// cwd_bogus_check=malloc(len);
|
||||
// cwd_bogus_check_len=len;
|
||||
// GetCurrentDirectory(len, cwd_bogus);
|
||||
//}
|
||||
|
||||
|
||||
|
||||
@@ -147,97 +144,142 @@ void _window_init_file()
|
||||
//}
|
||||
|
||||
namespace {
|
||||
class file_fs : public file {
|
||||
class file_fs : public filewrite {
|
||||
public:
|
||||
HANDLE handle;
|
||||
file_fs(const char * filename, HANDLE handle, size_t len) : file(filename, len), handle(handle) {}
|
||||
|
||||
file* clone()
|
||||
file_fs(cstring filename, HANDLE handle) : filewrite(filename), handle(handle)
|
||||
{
|
||||
HANDLE newhandle;
|
||||
DuplicateHandle(GetCurrentProcess(), this->handle, GetCurrentProcess(), &newhandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
||||
return new file_fs(this->filename, newhandle, this->len);
|
||||
LARGE_INTEGER size;
|
||||
GetFileSizeEx(this->handle, &size);
|
||||
this->len = size.QuadPart;
|
||||
}
|
||||
|
||||
size_t read(void* target, size_t start, size_t len)
|
||||
/*private*/ void seek(size_t pos)
|
||||
{
|
||||
char* target_c=(char*)target;
|
||||
//TODO: use OVERLAPPED to nuke the race condition
|
||||
LARGE_INTEGER pos;
|
||||
pos.QuadPart=start;
|
||||
SetFilePointerEx(this->handle, pos, NULL, FILE_BEGIN);
|
||||
LARGE_INTEGER lipos;
|
||||
lipos.QuadPart = pos;
|
||||
SetFilePointerEx(this->handle, lipos, NULL, FILE_BEGIN);
|
||||
}
|
||||
|
||||
size_t read(arrayvieww<byte> target, size_t start)
|
||||
{
|
||||
seek(start);
|
||||
DWORD actual;
|
||||
ReadFile(this->handle, target_c, len, &actual, NULL);
|
||||
ReadFile(this->handle, target.ptr(), target.size(), &actual, NULL);
|
||||
return actual;
|
||||
//>4GB lengths die if entered into this, but you shouldn't read() that at all. Use mmap().
|
||||
}
|
||||
|
||||
void* mmap(size_t start, size_t len)
|
||||
bool resize(size_t newsize)
|
||||
{
|
||||
HANDLE mem=CreateFileMapping(handle, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
void* ptr=MapViewOfFile(mem, FILE_MAP_READ, start>>16>>16, start&0xFFFFFFFF, len);
|
||||
CloseHandle(mem);
|
||||
return ptr;
|
||||
seek(newsize);
|
||||
if (SetEndOfFile(this->handle))
|
||||
{
|
||||
this->len = newsize;
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
void unmap(const void* data, size_t len) { UnmapViewOfFile((void*)data); }
|
||||
bool write(arrayview<byte> data, size_t start)
|
||||
{
|
||||
seek(start);
|
||||
DWORD actual;
|
||||
WriteFile(this->handle, data.ptr(), data.size(), &actual, NULL);
|
||||
if (actual==data.size())
|
||||
{
|
||||
this->len = max(this->len, start+data.size());
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
//stupid allocation granularity, its reason to exist (Alpha AXP) is long gone
|
||||
//and it never had a reason to exist outside Alpha anyways, porting to a new processor is always more than just recompiling
|
||||
//removing one of 9999 issues, especially one so rarely encountered as this, is not worth the trouble it causes
|
||||
//judging by https://blogs.msdn.microsoft.com/oldnewthing/20031008-00/?p=42223 ,
|
||||
// the allocation granularity is relevant for compiler/linker/etc authors only, who need to be aware of platform differences already
|
||||
//and even then, it's only relevant for file-backed executable pages, and as such, should only be enforced there
|
||||
//it does not belong in the kernel
|
||||
|
||||
//as an example of said trouble, consider the case where I want to map a .txt file, but ensure it's NUL terminated
|
||||
//if the size is not a multiple of the page size, the remainder is automatically zeroed
|
||||
//if the size is a multiple of the allocation granularity, I can VirtualAlloc an anonymous page there
|
||||
// (this could race, but I'll just unmap that and try again)
|
||||
//but if the size is a multiple of page size but not alloc gran, I can't get rid of the trap pages.
|
||||
|
||||
//or, more plausibly, consider the case of a program that wants to run on both Windows and Linux
|
||||
//the more differences, the harder, especially stupid ones like this
|
||||
|
||||
//yes, I just wrote a 1KB rant about a single extra F in this mask
|
||||
/*private*/ const size_t mmap_gran_mask = 0xFFFF;
|
||||
|
||||
/*private*/ arrayvieww<byte> mmap(bool write, size_t start, size_t len)
|
||||
{
|
||||
HANDLE mem = CreateFileMapping(handle, NULL, write ? PAGE_READWRITE : PAGE_READONLY, 0, 0, NULL);
|
||||
|
||||
size_t round = (start&mmap_gran_mask);
|
||||
start &= ~mmap_gran_mask;
|
||||
|
||||
byte* ptr = (byte*)MapViewOfFile(mem, write ? (FILE_MAP_READ|FILE_MAP_WRITE) : FILE_MAP_READ, start>>16>>16, start&0xFFFFFFFF, len+round);
|
||||
CloseHandle(mem);
|
||||
|
||||
if (ptr) return arrayvieww<byte>(ptr+round, len);
|
||||
else return arrayvieww<byte>(NULL, 0);
|
||||
}
|
||||
|
||||
arrayview<byte> mmap(size_t start, size_t len) { return mmap(false, start, len); }
|
||||
void unmap(arrayview<byte> data)
|
||||
{
|
||||
//docs say this should be identical to a MapViewOfFile return value, but it works fine with the low bits garbled
|
||||
UnmapViewOfFile(data.ptr());
|
||||
}
|
||||
|
||||
arrayvieww<byte> mmapw(size_t start, size_t len) { return mmap(true, start, len); }
|
||||
void unmapw(arrayvieww<byte> data) { unmap(data); }
|
||||
|
||||
~file_fs() { CloseHandle(handle); }
|
||||
};
|
||||
}
|
||||
|
||||
file* file::create_fs(const char * filename)
|
||||
#define FILE_SHARE_ALL (FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE)
|
||||
file* file::open_fs(cstring filename)
|
||||
{
|
||||
HANDLE file=CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
HANDLE file = CreateFile(filename, GENERIC_READ, FILE_SHARE_ALL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (file == INVALID_HANDLE_VALUE) return NULL;
|
||||
LARGE_INTEGER size;
|
||||
GetFileSizeEx(file, &size);
|
||||
return new file_fs(filename, file, size.QuadPart);
|
||||
return new file_fs(filename, file);
|
||||
}
|
||||
|
||||
#ifdef ARGUI_NONE
|
||||
file* file::create(const char * filename)
|
||||
filewrite* filewrite::open_fs(cstring filename, mode m)
|
||||
{
|
||||
return create_fs(filename);
|
||||
DWORD dispositions[] = { OPEN_ALWAYS, OPEN_EXISTING, CREATE_ALWAYS, CREATE_NEW };
|
||||
HANDLE file = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_ALL, NULL, dispositions[m], FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (file == INVALID_HANDLE_VALUE) return NULL;
|
||||
return new file_fs(filename, file);
|
||||
}
|
||||
#endif
|
||||
|
||||
//namespace {
|
||||
// class file_fs_wr : public filewrite {
|
||||
// public:
|
||||
// int fd;
|
||||
// file_fs_wr(int fd) : fd(fd) {}
|
||||
|
||||
// /*private*/ void alloc(size_t size)
|
||||
// {
|
||||
|
||||
// }
|
||||
|
||||
// /*private*/ void dealloc()
|
||||
// {
|
||||
|
||||
// }
|
||||
|
||||
// bool resize(size_t newsize)
|
||||
// {
|
||||
|
||||
// }
|
||||
|
||||
// void sync()
|
||||
// {
|
||||
|
||||
// }
|
||||
|
||||
// ~file_fs_wr()
|
||||
// {
|
||||
// sync();
|
||||
// dealloc();
|
||||
// close(this->fd);
|
||||
// }
|
||||
// };
|
||||
//};
|
||||
|
||||
//filewrite* filewrite::create_fs(const char * filename, bool truncate)
|
||||
//{
|
||||
|
||||
//}
|
||||
|
||||
bool filewrite::unlink_fs(cstring filename)
|
||||
{
|
||||
if (DeleteFile(filename)) return true;
|
||||
else return (GetLastError() == ERROR_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
//#ifdef ARGUI_NONE
|
||||
file* file::open(cstring filename)
|
||||
{
|
||||
return open_fs(filename);
|
||||
}
|
||||
|
||||
filewrite* filewrite::open(cstring filename, mode m)
|
||||
{
|
||||
return open_fs(filename, m);
|
||||
}
|
||||
|
||||
bool filewrite::unlink(cstring filename)
|
||||
{
|
||||
return unlink_fs(filename);
|
||||
}
|
||||
//#endif
|
||||
|
||||
void _window_init_file() {}
|
||||
#endif
|
||||
|
||||
220
arlib/file.h
220
arlib/file.h
@@ -1,162 +1,106 @@
|
||||
#pragma once
|
||||
#include "global.h"
|
||||
#include <string.h>
|
||||
|
||||
//These are implemented by the window manager; however, due to file operations being far more common than GUI, they're split off.
|
||||
|
||||
//Returns the working directory at the time of process launch.
|
||||
//The true working directory is set to something unusable, and the program may not change or use it.
|
||||
const char * window_get_cwd();
|
||||
|
||||
//Returns the process path, without the filename. Multiple calls will return the same pointer.
|
||||
const char * window_get_proc_path();
|
||||
//Converts a relative path (../roms/mario.smc) to an absolute path (/home/admin/roms/mario.smc).
|
||||
// Implemented by the window manager, so gvfs can be supported. If the file doesn't exist, it is
|
||||
// implementation defined whether the return value is a nonexistent path, or if it's NULL.
|
||||
//basepath is the directory you want to use as base, or a file in this directory. NULL means current
|
||||
// directory as set by window_cwd_enter, or that 'path' is expected absolute.
|
||||
//If allow_up is false, NULL will be returned if 'path' attempts to go up the directory tree (for example ../../../../../etc/passwd).
|
||||
//If path is absolute already, it will be returned (possibly canonicalized) if allow_up is true, or rejected otherwise.
|
||||
//Send it to free() once it's done.
|
||||
char * window_get_absolute_path(const char * basepath, const char * path, bool allow_up);
|
||||
//Converts any file path to something accessible on the local file system. The resulting path can
|
||||
// be both ugly and temporary, so only use it for file I/O, and store the absolute path instead.
|
||||
//It is not guaranteed that window_get_absolute_path can return the original path, or anything useful at all, if given the output of this.
|
||||
//It can return NULL, even for paths which file_read understands. If it doesn't, use free() when you're done.
|
||||
char * window_get_native_path(const char * path);
|
||||
#include "string.h"
|
||||
|
||||
class filewrite;
|
||||
class file : nocopy {
|
||||
private:
|
||||
file(){}
|
||||
protected:
|
||||
file(const char * filename) : filename(strdup(filename)) {}
|
||||
file(const char * filename, size_t(len)) : filename(strdup(filename)), len(len) {}
|
||||
file(cstring filename) : path(filename) {}
|
||||
file(cstring filename, size_t len) : path(filename), len(len) {}
|
||||
|
||||
//This one will create the file from the filesystem.
|
||||
//create() can simply return create_fs(filename), or can additionally support stuff like gvfs.
|
||||
static file* create_fs(const char * filename);
|
||||
static file* open_fs(cstring filename);
|
||||
|
||||
class mem;
|
||||
public:
|
||||
//While this object may look thread-safe, it isn't. One thread at the time only.
|
||||
//Interacting with mmap() results doesn't count as interaction.
|
||||
|
||||
static file* create(const char * filename);
|
||||
//A path refers to a directory if it ends with a slash, and file otherwise. Directories may not be open()ed, though listdir() is valid.
|
||||
static file* open(cstring filename);
|
||||
//Returns all items in the given directory path, as absolute paths.
|
||||
static array<string> listdir(cstring path);
|
||||
|
||||
char* filename;
|
||||
//If the input path is a directory, the basename is the last component after the final slash.
|
||||
static string dirname(cstring path);
|
||||
static string basename(cstring path);
|
||||
|
||||
//Changing these two is undefined behavior. Only the implementation may do that.
|
||||
string path;
|
||||
size_t len;
|
||||
|
||||
//The returned object is guaranteed equivalent to the given one, assuming the file is not changed or removed in the meanwhile.
|
||||
virtual file* clone() { return file::create(this->filename); }
|
||||
|
||||
virtual size_t read(void* target, size_t start, size_t len) = 0;
|
||||
size_t read(void* target, size_t len) { return this->read(target, 0, len); }
|
||||
virtual void* mmap(size_t start, size_t len) = 0;
|
||||
void* mmap() { return this->mmap(0, this->len); }
|
||||
virtual void unmap(const void* data, size_t len) = 0;
|
||||
|
||||
virtual ~file() { free(filename); }
|
||||
};
|
||||
|
||||
class file::mem : public file {
|
||||
void* data;
|
||||
public:
|
||||
mem(const char * filename, void* data, size_t len) : file(filename) { this->data=data; this->len=len; }
|
||||
|
||||
file* clone()
|
||||
//Reading outside the file will return partial results.
|
||||
virtual size_t read(arrayvieww<byte> target, size_t start) = 0;
|
||||
array<byte> read()
|
||||
{
|
||||
void* newdat=malloc(this->len);
|
||||
memcpy(newdat, this->data, this->len);
|
||||
return new file::mem(this->filename, newdat, this->len);
|
||||
array<byte> ret;
|
||||
ret.resize(this->len);
|
||||
size_t actual = this->read(ret, 0);
|
||||
ret.resize(actual);
|
||||
return ret;
|
||||
}
|
||||
static array<byte> read(cstring path)
|
||||
{
|
||||
autoptr<file> f = file::open(path);
|
||||
if (f) return f->read();
|
||||
else return NULL;
|
||||
}
|
||||
|
||||
size_t read(void* target, size_t start, size_t len) { memcpy(target, (char*)data+start, len); return len; }
|
||||
void* mmap(size_t start, size_t len) { return (char*)data+start; }
|
||||
void unmap(const void* data, size_t len) {}
|
||||
~mem() { free(data); }
|
||||
//Mappings must be deallocated before deleting the file object.
|
||||
//If the underlying file is changed, it's undefined whether the mappings update. To force an update, delete and recreate the mapping.
|
||||
//Mapping outside the file is undefined behavior.
|
||||
virtual arrayview<byte> mmap(size_t start, size_t len) = 0;
|
||||
arrayview<byte> mmap() { return this->mmap(0, this->len); }
|
||||
virtual void unmap(arrayview<byte> data) = 0;
|
||||
|
||||
virtual ~file() {}
|
||||
|
||||
//Mostly usable for debug purposes.
|
||||
static file* create_mem_view(arrayview<byte> data);
|
||||
static filewrite* create_mem_copy(array<byte> data);
|
||||
};
|
||||
|
||||
//virtual bool resize(size_t newsize) { return false; }
|
||||
////Sends all the data to the disk. Does not return until it's there.
|
||||
////The destructor also sends the data to disk, but does not guarantee that it's done immediately.
|
||||
////There may be more ways to send the file to disk, but this is not guaranteed either.
|
||||
//virtual void sync(){}
|
||||
|
||||
//These are implemented by the window manager, despite looking somewhat unrelated.
|
||||
//Support for absolute filenames is present.
|
||||
//Support for relative filenames will be rejected as much as possible. However, ../../../../../etc/passwd may work.
|
||||
//Other things, for example http://example.com/roms/snes/smw.sfc, may work too.
|
||||
//Directory separator is '/', extension separator is '.'.
|
||||
//file_read appends a '\0' to the output (whether the file is text or binary); this is not reported in the length.
|
||||
//Use free() on the return value from file_read().
|
||||
bool file_read(const char * filename, void* * data, size_t * len);
|
||||
bool file_write(const char * filename, const anyptr data, size_t len);
|
||||
bool file_read_to(const char * filename, anyptr data, size_t len);//If size differs, this one fails.
|
||||
|
||||
//Some simple wrappers for the above three.
|
||||
inline bool file_read_rel(const char * basepath, bool allow_up, const char * filename, void* * data, size_t * len)
|
||||
{
|
||||
char* path=window_get_absolute_path(basepath, filename, allow_up);
|
||||
if (!path) return false;
|
||||
bool ret=file_read(path, data, len);
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline bool file_write_rel(const char * basepath, bool allow_up, const char * filename, const anyptr data, size_t len)
|
||||
{
|
||||
char* path=window_get_absolute_path(basepath, filename, allow_up);
|
||||
if (!path) return false;
|
||||
bool ret=file_write(path, data, len);
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline bool file_read_to_rel(const char * basepath, bool allow_up, const char * filename, anyptr data, size_t len)
|
||||
{
|
||||
char* path=window_get_absolute_path(basepath, filename, allow_up);
|
||||
if (!path) return false;
|
||||
bool ret=file_read_to(path, data, len);
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//These will list the contents of a directory. The returned paths from window_find_next should be
|
||||
// sent to free(). The . and .. components will not be included; however, symlinks and other loops
|
||||
// are not guarded against. It is implementation defined whether hidden files are included. The
|
||||
// returned filenames are relative to the original path and contain no path information nor leading
|
||||
// or trailing slashes.
|
||||
void* file_find_create(const char * path);
|
||||
bool file_find_next(void* find, char* * path, bool * isdir);
|
||||
void file_find_close(void* find);
|
||||
|
||||
//If the window manager does not implement any non-native paths (like gvfs), it can use this one;
|
||||
// it's implemented by something that knows the local file system, but not the window manager.
|
||||
//There is no _window_native_get_native_path; since the local file system doesn't understand
|
||||
// anything except the local file system, it would only be able to return the input, or be
|
||||
// equivalent to _window_native_get_absolute_path, making it redundant and therefore useless.
|
||||
char * _window_native_get_absolute_path(const char * basepath, const char * path, bool allow_up);
|
||||
class filewrite : public file {
|
||||
protected:
|
||||
filewrite(cstring filename) : file(filename) {}
|
||||
filewrite(cstring filename, size_t len) : file(filename, len) {}
|
||||
|
||||
public:
|
||||
enum mode {
|
||||
m_default, // If the file exists, opens it. If it doesn't, creates a new file. (O_CREAT) (OPEN_ALWAYS)
|
||||
m_existing, // Fails if the file doesn't exist. (0) (OPEN_EXISTING)
|
||||
m_replace, // If the file exists, it's either deleted and recreated, or truncated. (O_CREAT|O_TRUNC) (CREATE_ALWAYS)
|
||||
m_create_excl, // Fails if the file does exist. (O_CREAT|O_EXCL) (CREATE_NEW)
|
||||
};
|
||||
protected:
|
||||
//These refer to the physical file system. The public versions can forward to these, or can additionally support stuff like gvfs.
|
||||
static filewrite* open_fs(cstring filename, mode m = m_default);
|
||||
static bool unlink_fs(cstring filename);
|
||||
public:
|
||||
static filewrite* open(cstring filename, mode m = m_default);
|
||||
static bool unlink(cstring filename); // Returns whether the file is now gone. If the file didn't exist, returns true.
|
||||
|
||||
virtual bool resize(size_t newsize) = 0; // May only be used if there are no mappings alive, not even read-only.
|
||||
//Writes outside the file will extend it. If the write starts after the current size, it's zero extended. Includes mmapw.
|
||||
virtual bool write(arrayview<byte> data, size_t start = 0) = 0;
|
||||
virtual bool replace(arrayview<byte> data) { return resize(data.size()) && write(data); }
|
||||
bool replace(cstring data) { return replace(data.bytes()); }
|
||||
bool write(cstring data) { return write(data.bytes()); }
|
||||
|
||||
static bool write(cstring path, arrayview<byte> data)
|
||||
{
|
||||
autoptr<filewrite> f = filewrite::open(path, m_replace);
|
||||
return f->write(data);
|
||||
}
|
||||
|
||||
//The only allowed method on a file object that has an existing writable mapping is unmapw.
|
||||
//Fails if it goes outside the file; use resize().
|
||||
virtual arrayvieww<byte> mmapw(size_t start, size_t len) = 0;
|
||||
arrayvieww<byte> mmapw() { return this->mmapw(0, this->len); }
|
||||
virtual void unmapw(arrayvieww<byte> data) = 0;
|
||||
};
|
||||
|
||||
void _window_init_file();
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//The above was defined before Arlib was split off from minir, and is unlikely to still work.
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//TODO before all of this: create string class
|
||||
//TODO before all of this: create container classes (let API roughly mirror C#)
|
||||
//TODO: file_find should return an array
|
||||
//TODO: create a path sanitizer that canonicalizes a path
|
||||
//TODO: create a path verifier that checks if a path is within a specified directory
|
||||
//TODO: create a path policy that checks if a path is within one of many allowed directories
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13,8 +13,24 @@
|
||||
//the namespace pollution this causes is massive, but without it, there's a bunch of functions that
|
||||
// just tail call kernel32.dll. With it, they can be inlined.
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
# endif
|
||||
# define strcasecmp _stricmp
|
||||
# define strncasecmp _strnicmp
|
||||
# ifdef _MSC_VER
|
||||
# define _CRT_NONSTDC_NO_DEPRECATE
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# undef interface // screw that, I'm not interested in COM shittery
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4800) // forcing value to bool 'true' or 'false' (performance warning)
|
||||
#endif
|
||||
|
||||
#ifndef __has_include
|
||||
#define __has_include(x) false
|
||||
#endif
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
@@ -31,6 +47,8 @@
|
||||
#include "function.h"
|
||||
#include <utility>
|
||||
|
||||
#define byte uint8_t
|
||||
|
||||
typedef void(*funcptr)();
|
||||
|
||||
//Note to anyone interested in reusing these objects:
|
||||
@@ -81,7 +99,7 @@ template<typename T, size_t N> char(&ARRAY_SIZE_CORE(T(&x)[N]))[N];
|
||||
#define PPFE_MAP_NEXT0(test, next, ...) next PPFE_MAP_OUT
|
||||
#ifdef _MSC_VER
|
||||
//this version doesn't work on GCC, it makes PPFE_MAP0 not get expanded the second time and quite effectively stops everything.
|
||||
//but completely unknown guy says it's required on MSVC, so I'll trust that and ifdef it.
|
||||
//but completely unknown guy says it's required on MSVC, so I'll trust that and ifdef it
|
||||
#define PPFE_MAP_NEXT1(test, next) PPFE_EVAL0(PPFE_MAP_NEXT0 (test, next, 0))
|
||||
#else
|
||||
#define PPFE_MAP_NEXT1(test, next) PPFE_MAP_NEXT0 (test, next, 0)
|
||||
@@ -95,6 +113,8 @@ template<typename T, size_t N> char(&ARRAY_SIZE_CORE(T(&x)[N]))[N];
|
||||
//PPFOREACH(STRING, foo, bar, baz)
|
||||
//limited to 365 entries, but that's enough.
|
||||
|
||||
|
||||
|
||||
//requirements:
|
||||
//- static_assert(false) throws something at compile time
|
||||
//- multiple static_assert(true) works
|
||||
@@ -109,7 +129,7 @@ template<typename T, size_t N> char(&ARRAY_SIZE_CORE(T(&x)[N]))[N];
|
||||
//optional:
|
||||
//- (PASS) works in a template, even if the template isn't instantiated, if the condition isn't dependent on the types
|
||||
//- (FAIL) works if compiled as C (tried to design an alternate implementation and ifdef it, but nothing works inside structs)
|
||||
//- (FAIL) can name assertions, if desired
|
||||
//- (PASS) can name assertions, if desired (only under C++11)
|
||||
#ifdef __GNUC__
|
||||
#define MAYBE_UNUSED __attribute__((__unused__)) // shut up, stupid warnings
|
||||
#define TYPENAME_IF_GCC typename // gcc requires this. msvc rejects this.
|
||||
@@ -118,6 +138,7 @@ template<typename T, size_t N> char(&ARRAY_SIZE_CORE(T(&x)[N]))[N];
|
||||
#define TYPENAME_IF_GCC
|
||||
#endif
|
||||
|
||||
#if __cplusplus < 201999 // TODO: replace with real C++17
|
||||
#if __cplusplus < 201103
|
||||
template<bool x> struct static_assert_t;
|
||||
template<> struct static_assert_t<true> { struct STATIC_ASSERTION_FAILED {}; };
|
||||
@@ -125,15 +146,20 @@ template<> struct static_assert_t<false> {};
|
||||
//#define static_assert(expr)
|
||||
// typedef TYPENAME_IF_NEEDED static_assert_t<(bool)(expr)>::STATIC_ASSERTION_FAILED
|
||||
// JOIN(static_assertion_, __COUNTER__) MAYBE_UNUSED;
|
||||
#define static_assert(expr) \
|
||||
#define static_assert_c(expr, name, ...) \
|
||||
enum { \
|
||||
JOIN(static_assertion_, __COUNTER__) = \
|
||||
sizeof(TYPENAME_IF_GCC static_assert_t<(bool)(expr)>::STATIC_ASSERTION_FAILED) \
|
||||
} MAYBE_UNUSED
|
||||
#else
|
||||
#define static_assert(expr) static_assert(expr, #expr)
|
||||
#define static_assert_c(expr, name, ...) static_assert(expr, name)
|
||||
#endif
|
||||
|
||||
#define static_assert_name(x, ...) #x
|
||||
#define static_assert(...) static_assert_c(__VA_ARGS__, static_assert_name(__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
|
||||
//almost C version (fails inside structs)
|
||||
//#define static_assert(expr) \
|
||||
// typedef char JOIN(static_assertion_, __COUNTER__)[(expr)?1:-1]
|
||||
@@ -163,7 +189,7 @@ typedef void* anyptr;
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdlib.h> // needed because otherwise I get errors from malloc_check being redeclared.
|
||||
#include <stdlib.h> // needed because otherwise I get errors from malloc_check being redeclared
|
||||
anyptr malloc_check(size_t size);
|
||||
anyptr try_malloc(size_t size);
|
||||
#define malloc malloc_check
|
||||
@@ -176,7 +202,7 @@ anyptr try_calloc(size_t size, size_t count);
|
||||
void malloc_assert(bool cond); // if the condition is false, the malloc failure handler is called
|
||||
|
||||
|
||||
//if I cast it to void, that means I do not care, so shut the hell up about warn_unused_result.
|
||||
//if I cast it to void, that means I do not care, so shut the hell up about warn_unused_result
|
||||
template<typename T> static inline void ignore(T t) {}
|
||||
|
||||
template<typename T> static T min(const T& a) { return a; }
|
||||
@@ -190,7 +216,7 @@ template<typename T, typename... Args> static T min(const T& a, Args... args)
|
||||
template<typename T> static T max(const T& a) { return a; }
|
||||
template<typename T, typename... Args> static T max(const T& a, Args... args)
|
||||
{
|
||||
const T& b = min(args...);
|
||||
const T& b = max(args...);
|
||||
if (a < b) return b;
|
||||
else return a;
|
||||
}
|
||||
@@ -225,7 +251,9 @@ template<typename T, typename... Args> static T max(const T& a, Args... args)
|
||||
|
||||
|
||||
class empty {
|
||||
int x[];
|
||||
#ifndef _MSC_VER // error C2503: base classes cannot contain zero-sized arrays
|
||||
int __zero_size[]; // this base is used only by nocopy/nomove, and they're only used by
|
||||
#endif // nonzero objects which will optimize the empty base class anyways
|
||||
};
|
||||
|
||||
class nocopy : empty {
|
||||
@@ -234,8 +262,11 @@ protected:
|
||||
~nocopy() {}
|
||||
nocopy(const nocopy&) = delete;
|
||||
const nocopy& operator=(const nocopy&) = delete;
|
||||
#if !defined(_MSC_VER) || _MSC_VER >= 1900 // error C2610: is not a special member function which can be defaulted
|
||||
// defaulting the copies deletes the moves on gcc, but does nothing on msvc2013; known bug
|
||||
nocopy(nocopy&&) = default;
|
||||
nocopy& operator=(nocopy&&) = default;
|
||||
#endif
|
||||
};
|
||||
|
||||
class nomove : empty {
|
||||
@@ -255,21 +286,28 @@ public:
|
||||
autoptr() : ptr(NULL) {}
|
||||
autoptr(T* ptr) : ptr(ptr) {}
|
||||
autoptr(autoptr<T>&& other) { ptr=other.ptr; other.ptr=NULL; }
|
||||
autoptr<T>& operator=(T* ptr) { delete this->ptr; this->ptr=ptr; }
|
||||
autoptr<T>& operator=(autoptr<T>&& other) { delete this->ptr; ptr=other.ptr; other.ptr=NULL; }
|
||||
autoptr<T>& operator=(T* ptr) { delete this->ptr; this->ptr=ptr; return *this; }
|
||||
autoptr<T>& operator=(autoptr<T>&& other) { delete this->ptr; ptr=other.ptr; other.ptr=NULL; return *this; }
|
||||
T* release() { T* ret = ptr; ptr = NULL; return ret; }
|
||||
T* operator->() { return ptr; }
|
||||
T& operator*() { return *ptr; }
|
||||
operator T*() { return ptr; }
|
||||
explicit operator bool() { return ptr; }
|
||||
~autoptr() { delete ptr; }
|
||||
};
|
||||
|
||||
class null_t_impl {};
|
||||
#define null_t null_t_impl* // random pointer type nobody will ever use
|
||||
|
||||
|
||||
|
||||
#if defined(__linux__) || GCC_VERSION >= 40900
|
||||
#define asprintf(...) malloc_assert(asprintf(__VA_ARGS__) >= 0)
|
||||
#else
|
||||
void asprintf(char * * ptr, const char * fmt, ...);
|
||||
#endif
|
||||
|
||||
//#if defined(__linux__) || GCC_VERSION >= 40900
|
||||
//#define asprintf(...) malloc_assert(asprintf(__VA_ARGS__) >= 0)
|
||||
//#else
|
||||
//void asprintf(char * * ptr, const char * fmt, ...);
|
||||
//#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
void* memmem(const void * haystack, size_t haystacklen, const void * needle, size_t needlelen);
|
||||
#endif
|
||||
@@ -286,8 +324,8 @@ template<typename T> static inline T bitround(T in)
|
||||
in|=in>>2;
|
||||
in|=in>>4;
|
||||
in|=in>>8;
|
||||
if (sizeof(T)>2) in|=in>>8>>8;
|
||||
if (sizeof(T)>4) in|=in>>8>>8>>8>>8;
|
||||
if (sizeof(in)>2) in|=in>>8>>8; // double shift to shut up bitshift-out-of-range warnings
|
||||
if (sizeof(in)>4) in|=in>>8>>8>>8>>8;
|
||||
in++;
|
||||
return in;
|
||||
}
|
||||
|
||||
@@ -454,29 +454,64 @@ class widget_canvas;
|
||||
|
||||
|
||||
struct widget_viewport::impl {
|
||||
bool hide_mouse;
|
||||
bool hide_mouse_timer_active;
|
||||
Window child;
|
||||
|
||||
guint32 hide_mouse_at;
|
||||
GdkCursor* hidden_cursor;
|
||||
GdkRectangle lastpos;
|
||||
|
||||
function<void(const char * const * filenames)> on_file_drop;
|
||||
function<void(unsigned int width, unsigned int height)> onresize;
|
||||
function<void()> ondestroy;
|
||||
|
||||
//bool hide_mouse;
|
||||
//bool hide_mouse_timer_active;
|
||||
//
|
||||
//guint32 hide_mouse_at;
|
||||
//GdkCursor* hidden_cursor;
|
||||
//
|
||||
//function<void(const char * const * filenames)> on_file_drop;
|
||||
};
|
||||
|
||||
static void viewport_submit_resize(widget_viewport::impl* m)
|
||||
{
|
||||
if (m->child && m->lastpos.width>0)
|
||||
{
|
||||
XMoveResizeWindow(window_x11.display, m->child,
|
||||
m->lastpos.x, m->lastpos.y,
|
||||
m->lastpos.width, m->lastpos.height);
|
||||
m->onresize(m->lastpos.width, m->lastpos.height);
|
||||
}
|
||||
}
|
||||
|
||||
static void viewport_resize_handler(GtkWidget* widget, GdkRectangle* allocation, gpointer user_data)
|
||||
{
|
||||
widget_viewport* obj = (widget_viewport*)user_data;
|
||||
widget_viewport::impl* m = obj->m;
|
||||
|
||||
if (memcmp(&m->lastpos, allocation, sizeof(GdkRectangle)) != 0)
|
||||
{
|
||||
m->lastpos = *allocation;
|
||||
viewport_submit_resize(m);
|
||||
}
|
||||
}
|
||||
|
||||
widget_viewport::widget_viewport(unsigned int width, unsigned int height) : m(new impl)
|
||||
{
|
||||
widget=gtk_drawing_area_new();
|
||||
widthprio=0;
|
||||
heightprio=0;
|
||||
widget = gtk_drawing_area_new();
|
||||
widthprio = 0;
|
||||
heightprio = 0;
|
||||
|
||||
m->hide_mouse_at=0;
|
||||
m->hidden_cursor=NULL;
|
||||
//m->hide_mouse_at = 0;
|
||||
//m->hidden_cursor = NULL;
|
||||
gtk_widget_set_size_request(GTK_WIDGET(widget), width, height);
|
||||
|
||||
m->child = 0;
|
||||
memset(&m->lastpos, -1, sizeof(m->lastpos));
|
||||
g_signal_connect(widget, "size-allocate", G_CALLBACK(viewport_resize_handler), this);
|
||||
}
|
||||
|
||||
widget_viewport::~widget_viewport()
|
||||
{
|
||||
if (m->hidden_cursor) g_object_unref(m->hidden_cursor);
|
||||
//if (m->hidden_cursor) g_object_unref(m->hidden_cursor);
|
||||
m->ondestroy();
|
||||
delete m;
|
||||
}
|
||||
|
||||
@@ -489,88 +524,97 @@ widget_viewport* widget_viewport::resize(unsigned int width, unsigned int height
|
||||
return this;
|
||||
}
|
||||
|
||||
uintptr_t widget_viewport::get_window_handle()
|
||||
uintptr_t widget_viewport::get_parent()
|
||||
{
|
||||
gtk_widget_realize(GTK_WIDGET(widget));
|
||||
//this won't work on anything except X11, but should be trivial to create an equivalent for.
|
||||
gtk_widget_realize(GTK_WIDGET(this->widget));
|
||||
return gdk_x11_window_get_xid(gtk_widget_get_window(GTK_WIDGET(widget)));
|
||||
}
|
||||
|
||||
void widget_viewport::get_position(int * x, int * y, unsigned int * width, unsigned int * height)
|
||||
void widget_viewport::set_child(uintptr_t windowhandle,
|
||||
function<void(unsigned int width, unsigned int height)> onresize,
|
||||
function<void()> ondestroy)
|
||||
{
|
||||
gtk_widget_realize(GTK_WIDGET(widget));
|
||||
GdkWindow* window=gtk_widget_get_window(GTK_WIDGET(widget));
|
||||
gdk_window_get_origin(window, x, y);
|
||||
if (width) *width=gdk_window_get_width(window);
|
||||
if (height) *height=gdk_window_get_height(window);
|
||||
//if (x) *height=gdk_window_get_height(window);
|
||||
//if (y) *height=gdk_window_get_height(window);
|
||||
m->child = (Window)windowhandle;
|
||||
m->onresize = onresize;
|
||||
m->ondestroy = ondestroy;
|
||||
viewport_submit_resize(m);
|
||||
}
|
||||
|
||||
static void viewport_set_hide_cursor_now(widget_viewport* obj, bool hide)
|
||||
{
|
||||
GdkWindow* gdkwindow=gtk_widget_get_window(GTK_WIDGET(obj->widget));
|
||||
if (gdkwindow) gdk_window_set_cursor(gdkwindow, hide ? obj->m->hidden_cursor : NULL);
|
||||
}
|
||||
//void widget_viewport::get_position(int * x, int * y, unsigned int * width, unsigned int * height)
|
||||
//{
|
||||
// gtk_widget_realize(GTK_WIDGET(widget));
|
||||
// GdkWindow* window=gtk_widget_get_window(GTK_WIDGET(widget));
|
||||
// gdk_window_get_origin(window, x, y);
|
||||
// if (width) *width=gdk_window_get_width(window);
|
||||
// if (height) *height=gdk_window_get_height(window);
|
||||
// //if (x) *height=gdk_window_get_height(window);
|
||||
// //if (y) *height=gdk_window_get_height(window);
|
||||
//}
|
||||
|
||||
static gboolean viewport_mouse_timeout(gpointer user_data)
|
||||
{
|
||||
widget_viewport* obj=(widget_viewport*)user_data;
|
||||
|
||||
guint32 now=g_get_monotonic_time()/1000;
|
||||
if (now >= obj->m->hide_mouse_at)
|
||||
{
|
||||
obj->m->hide_mouse_timer_active=false;
|
||||
viewport_set_hide_cursor_now(obj, obj->m->hide_mouse);
|
||||
}
|
||||
else
|
||||
{
|
||||
guint32 remaining=obj->m->hide_mouse_at-now+10;
|
||||
g_timeout_add(remaining, viewport_mouse_timeout, obj);
|
||||
}
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static gboolean viewport_mouse_move_handler(GtkWidget* widget, GdkEvent* event, gpointer user_data)
|
||||
{
|
||||
widget_viewport* obj=(widget_viewport*)user_data;
|
||||
|
||||
obj->m->hide_mouse_at=g_get_monotonic_time()/1000 + 990;
|
||||
if (!obj->m->hide_mouse_timer_active)
|
||||
{
|
||||
obj->m->hide_mouse_timer_active=true;
|
||||
g_timeout_add(1000, viewport_mouse_timeout, obj);
|
||||
viewport_set_hide_cursor_now(obj, false);
|
||||
}
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
widget_viewport* widget_viewport::set_hide_cursor(bool hide)
|
||||
{
|
||||
if (m->hide_mouse_at && g_get_monotonic_time()/1000 >= m->hide_mouse_at)
|
||||
{
|
||||
viewport_set_hide_cursor_now(this, hide);
|
||||
}
|
||||
|
||||
m->hide_mouse=hide;
|
||||
if (!hide || m->hide_mouse_at) return this;
|
||||
|
||||
if (!m->hidden_cursor) m->hidden_cursor=gdk_cursor_new(GDK_BLANK_CURSOR);
|
||||
|
||||
gtk_widget_add_events(GTK_WIDGET(widget), GDK_POINTER_MOTION_MASK);
|
||||
g_signal_connect(widget, "motion-notify-event", G_CALLBACK(viewport_mouse_move_handler), this);
|
||||
|
||||
//seems to not exist in gtk+ 3.8
|
||||
//and gdk_event_request_motions does nothing, either - am I building for an older GTK+ than I'm using?
|
||||
//gdk_window_set_event_compression(gtk_widget_get_window(this->i._base.widget), false);
|
||||
|
||||
m->hide_mouse_timer_active=false;
|
||||
viewport_mouse_move_handler(NULL, NULL, this);
|
||||
|
||||
return this;
|
||||
}
|
||||
//static void viewport_set_hide_cursor_now(widget_viewport* obj, bool hide)
|
||||
//{
|
||||
// GdkWindow* gdkwindow=gtk_widget_get_window(GTK_WIDGET(obj->widget));
|
||||
// if (gdkwindow) gdk_window_set_cursor(gdkwindow, hide ? obj->m->hidden_cursor : NULL);
|
||||
//}
|
||||
//
|
||||
//static gboolean viewport_mouse_timeout(gpointer user_data)
|
||||
//{
|
||||
// widget_viewport* obj=(widget_viewport*)user_data;
|
||||
//
|
||||
// guint32 now=g_get_monotonic_time()/1000;
|
||||
// if (now >= obj->m->hide_mouse_at)
|
||||
// {
|
||||
// obj->m->hide_mouse_timer_active=false;
|
||||
// viewport_set_hide_cursor_now(obj, obj->m->hide_mouse);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// guint32 remaining=obj->m->hide_mouse_at-now+10;
|
||||
// g_timeout_add(remaining, viewport_mouse_timeout, obj);
|
||||
// }
|
||||
//
|
||||
// return G_SOURCE_REMOVE;
|
||||
//}
|
||||
//
|
||||
//static gboolean viewport_mouse_move_handler(GtkWidget* widget, GdkEvent* event, gpointer user_data)
|
||||
//{
|
||||
// widget_viewport* obj=(widget_viewport*)user_data;
|
||||
//
|
||||
// obj->m->hide_mouse_at=g_get_monotonic_time()/1000 + 990;
|
||||
// if (!obj->m->hide_mouse_timer_active)
|
||||
// {
|
||||
// obj->m->hide_mouse_timer_active=true;
|
||||
// g_timeout_add(1000, viewport_mouse_timeout, obj);
|
||||
// viewport_set_hide_cursor_now(obj, false);
|
||||
// }
|
||||
//
|
||||
// return G_SOURCE_CONTINUE;
|
||||
//}
|
||||
//
|
||||
//widget_viewport* widget_viewport::set_hide_cursor(bool hide)
|
||||
//{
|
||||
// if (m->hide_mouse_at && g_get_monotonic_time()/1000 >= m->hide_mouse_at)
|
||||
// {
|
||||
// viewport_set_hide_cursor_now(this, hide);
|
||||
// }
|
||||
//
|
||||
// m->hide_mouse=hide;
|
||||
// if (!hide || m->hide_mouse_at) return this;
|
||||
//
|
||||
// if (!m->hidden_cursor) m->hidden_cursor=gdk_cursor_new(GDK_BLANK_CURSOR);
|
||||
//
|
||||
// gtk_widget_add_events(GTK_WIDGET(widget), GDK_POINTER_MOTION_MASK);
|
||||
// g_signal_connect(widget, "motion-notify-event", G_CALLBACK(viewport_mouse_move_handler), this);
|
||||
//
|
||||
// //seems to not exist in gtk+ 3.8
|
||||
// //and gdk_event_request_motions does nothing, either - am I building for an older GTK+ than I'm using?
|
||||
// //gdk_window_set_event_compression(gtk_widget_get_window(this->i._base.widget), false);
|
||||
//
|
||||
// m->hide_mouse_timer_active=false;
|
||||
// viewport_mouse_move_handler(NULL, NULL, this);
|
||||
//
|
||||
// return this;
|
||||
//}
|
||||
|
||||
/*
|
||||
void (*keyboard_cb)(struct window * subject, unsigned int keycode, void* userdata);
|
||||
@@ -593,66 +637,66 @@ static void set_kb_callback(struct window * this_,
|
||||
}
|
||||
*/
|
||||
|
||||
static void viewport_drop_handler(GtkWidget* widget, GdkDragContext* drag_context, gint x, gint y,
|
||||
GtkSelectionData* selection_data, guint info, guint time, gpointer user_data)
|
||||
{
|
||||
widget_viewport* obj=(widget_viewport*)user_data;
|
||||
if (!selection_data || !gtk_selection_data_get_length(selection_data))
|
||||
{
|
||||
gtk_drag_finish(drag_context, FALSE, FALSE, time);
|
||||
return;
|
||||
}
|
||||
|
||||
const char * data=(gchar*)gtk_selection_data_get_data(selection_data);
|
||||
int numstr=0;
|
||||
for (int i=0;data[i];i++)
|
||||
{
|
||||
if (data[i]=='\n') numstr++;
|
||||
}
|
||||
|
||||
char* datacopy=strdup(data);
|
||||
char** strings=malloc(sizeof(char*)*(numstr+1));
|
||||
char* last=datacopy;
|
||||
int strnum=0;
|
||||
for (int i=0;datacopy[i];i++)
|
||||
{
|
||||
if (datacopy[i]=='\r') datacopy[i]='\0';//where did those come from? this isn't Windows, we shouldn't be getting Windows-isms.
|
||||
if (datacopy[i]=='\n')
|
||||
{
|
||||
datacopy[i]='\0';
|
||||
strings[strnum]=window_get_absolute_path(NULL, last, true);
|
||||
last=datacopy+i+1;
|
||||
strnum++;
|
||||
}
|
||||
}
|
||||
strings[numstr]=NULL;
|
||||
free(datacopy);
|
||||
|
||||
obj->m->on_file_drop(strings);
|
||||
|
||||
for (int i=0;strings[i];i++) free(strings[i]);
|
||||
free(strings);
|
||||
gtk_drag_finish(drag_context, TRUE, FALSE, time);
|
||||
}
|
||||
|
||||
widget_viewport* widget_viewport::set_support_drop(function<void(const char * const * filenames)> on_file_drop)
|
||||
{
|
||||
GtkTargetList* list=gtk_target_list_new(NULL, 0);
|
||||
gtk_target_list_add_uri_targets(list, 0);
|
||||
|
||||
int n_targets;
|
||||
GtkTargetEntry* targets=gtk_target_table_new_from_list(list, &n_targets);
|
||||
//GTK_DEST_DEFAULT_MOTION|GTK_DEST_DEFAULT_DROP
|
||||
gtk_drag_dest_set(GTK_WIDGET(widget), GTK_DEST_DEFAULT_ALL, targets,n_targets, GDK_ACTION_COPY);
|
||||
|
||||
gtk_target_table_free(targets, n_targets);
|
||||
gtk_target_list_unref(list);
|
||||
|
||||
g_signal_connect(widget, "drag-data-received", G_CALLBACK(viewport_drop_handler), this);
|
||||
m->on_file_drop=on_file_drop;
|
||||
|
||||
return this;
|
||||
}
|
||||
//static void viewport_drop_handler(GtkWidget* widget, GdkDragContext* drag_context, gint x, gint y,
|
||||
// GtkSelectionData* selection_data, guint info, guint time, gpointer user_data)
|
||||
//{
|
||||
// widget_viewport* obj=(widget_viewport*)user_data;
|
||||
// if (!selection_data || !gtk_selection_data_get_length(selection_data))
|
||||
// {
|
||||
// gtk_drag_finish(drag_context, FALSE, FALSE, time);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// const char * data=(gchar*)gtk_selection_data_get_data(selection_data);
|
||||
// int numstr=0;
|
||||
// for (int i=0;data[i];i++)
|
||||
// {
|
||||
// if (data[i]=='\n') numstr++;
|
||||
// }
|
||||
//
|
||||
// char* datacopy=strdup(data);
|
||||
// char** strings=malloc(sizeof(char*)*(numstr+1));
|
||||
// char* last=datacopy;
|
||||
// int strnum=0;
|
||||
// for (int i=0;datacopy[i];i++)
|
||||
// {
|
||||
// if (datacopy[i]=='\r') datacopy[i]='\0';//where did those come from? this isn't Windows, we shouldn't be getting Windows-isms.
|
||||
// if (datacopy[i]=='\n')
|
||||
// {
|
||||
// datacopy[i]='\0';
|
||||
// strings[strnum]=window_get_absolute_path(NULL, last, true);
|
||||
// last=datacopy+i+1;
|
||||
// strnum++;
|
||||
// }
|
||||
// }
|
||||
// strings[numstr]=NULL;
|
||||
// free(datacopy);
|
||||
//
|
||||
// obj->m->on_file_drop(strings);
|
||||
//
|
||||
// for (int i=0;strings[i];i++) free(strings[i]);
|
||||
// free(strings);
|
||||
// gtk_drag_finish(drag_context, TRUE, FALSE, time);
|
||||
//}
|
||||
//
|
||||
//widget_viewport* widget_viewport::set_support_drop(function<void(const char * const * filenames)> on_file_drop)
|
||||
//{
|
||||
// GtkTargetList* list=gtk_target_list_new(NULL, 0);
|
||||
// gtk_target_list_add_uri_targets(list, 0);
|
||||
//
|
||||
// int n_targets;
|
||||
// GtkTargetEntry* targets=gtk_target_table_new_from_list(list, &n_targets);
|
||||
// //GTK_DEST_DEFAULT_MOTION|GTK_DEST_DEFAULT_DROP
|
||||
// gtk_drag_dest_set(GTK_WIDGET(widget), GTK_DEST_DEFAULT_ALL, targets,n_targets, GDK_ACTION_COPY);
|
||||
//
|
||||
// gtk_target_table_free(targets, n_targets);
|
||||
// gtk_target_list_unref(list);
|
||||
//
|
||||
// g_signal_connect(widget, "drag-data-received", G_CALLBACK(viewport_drop_handler), this);
|
||||
// m->on_file_drop=on_file_drop;
|
||||
//
|
||||
// return this;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -76,123 +76,123 @@ g_log_set_always_fatal((GLogLevelFlags)(G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING
|
||||
errno=0;
|
||||
}
|
||||
|
||||
file* file::create(const char * filename)
|
||||
{
|
||||
//TODO
|
||||
return create_fs(filename);
|
||||
}
|
||||
//file* file::create(const char * filename)
|
||||
//{
|
||||
// //TODO
|
||||
// return create_fs(filename);
|
||||
//}
|
||||
|
||||
static void * mem_from_g_alloc(void * mem, size_t size)
|
||||
{
|
||||
if (g_mem_is_system_malloc()) return mem;
|
||||
|
||||
if (!size) size=strlen((char*)mem)+1;
|
||||
|
||||
void * ret=malloc(size);
|
||||
memcpy(ret, mem, size);
|
||||
g_free(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//enum mbox_sev { mb_info, mb_warn, mb_err };
|
||||
//enum mbox_btns { mb_ok, mb_okcancel, mb_yesno };
|
||||
bool window_message_box(const char * text, const char * title, enum mbox_sev severity, enum mbox_btns buttons)
|
||||
{
|
||||
//"Please note that GTK_BUTTONS_OK, GTK_BUTTONS_YES_NO and GTK_BUTTONS_OK_CANCEL are discouraged by the GNOME HIG."
|
||||
//I do not listen to advise without a rationale. Tell me which section it violates, and I'll consider it.
|
||||
GtkMessageType sev[3]={ GTK_MESSAGE_OTHER, GTK_MESSAGE_WARNING, GTK_MESSAGE_ERROR };
|
||||
GtkButtonsType btns[3]={ GTK_BUTTONS_OK, GTK_BUTTONS_OK_CANCEL, GTK_BUTTONS_YES_NO };
|
||||
GtkWidget* dialog=gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, sev[severity], btns[buttons], "%s", text);
|
||||
gint ret=gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
gtk_widget_destroy(dialog);
|
||||
return (ret==GTK_RESPONSE_ACCEPT || ret==GTK_RESPONSE_OK || ret==GTK_RESPONSE_YES);
|
||||
}
|
||||
|
||||
const char * const * window_file_picker(struct window * parent,
|
||||
const char * title,
|
||||
const char * const * extensions,
|
||||
const char * extdescription,
|
||||
bool dylib,
|
||||
bool multiple)
|
||||
{
|
||||
static char * * ret=NULL;
|
||||
if (ret)
|
||||
{
|
||||
char * * del=ret;
|
||||
while (*del)
|
||||
{
|
||||
free(*del);
|
||||
del++;
|
||||
}
|
||||
free(ret);
|
||||
ret=NULL;
|
||||
}
|
||||
|
||||
GtkFileChooser* dialog=GTK_FILE_CHOOSER(
|
||||
gtk_file_chooser_dialog_new(
|
||||
title,
|
||||
GTK_WINDOW(parent?(void*)parent->_get_handle():NULL),
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
"_Cancel",
|
||||
GTK_RESPONSE_CANCEL,
|
||||
"_Open",
|
||||
GTK_RESPONSE_ACCEPT,
|
||||
NULL));
|
||||
gtk_file_chooser_set_select_multiple(dialog, multiple);
|
||||
gtk_file_chooser_set_local_only(dialog, dylib);
|
||||
|
||||
GtkFileFilter* filter;
|
||||
|
||||
if (*extensions)
|
||||
{
|
||||
filter=gtk_file_filter_new();
|
||||
gtk_file_filter_set_name(filter, extdescription);
|
||||
char extstr[64];
|
||||
extstr[0]='*';
|
||||
extstr[1]='.';
|
||||
while (*extensions)
|
||||
{
|
||||
strcpy(extstr+2, *extensions+(**extensions=='.'));
|
||||
gtk_file_filter_add_pattern(filter, extstr);
|
||||
extensions++;
|
||||
}
|
||||
gtk_file_chooser_add_filter(dialog, filter);
|
||||
}
|
||||
|
||||
filter=gtk_file_filter_new();
|
||||
gtk_file_filter_set_name(filter, "All files");
|
||||
gtk_file_filter_add_pattern(filter, "*");
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
|
||||
|
||||
if (gtk_dialog_run(GTK_DIALOG(dialog))!=GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
gtk_widget_destroy(GTK_WIDGET(dialog));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GSList * list=gtk_file_chooser_get_uris(dialog);
|
||||
gtk_widget_destroy(GTK_WIDGET(dialog));
|
||||
unsigned int listlen=g_slist_length(list);
|
||||
if (!listlen)
|
||||
{
|
||||
g_slist_free(list);
|
||||
return NULL;
|
||||
}
|
||||
ret=malloc(sizeof(char*)*(listlen+1));
|
||||
|
||||
char * * retcopy=ret;
|
||||
GSList * listcopy=list;
|
||||
while (listcopy)
|
||||
{
|
||||
*retcopy=window_get_absolute_path(NULL, (char*)listcopy->data, true);
|
||||
g_free(listcopy->data);
|
||||
retcopy++;
|
||||
listcopy=listcopy->next;
|
||||
}
|
||||
ret[listlen]=NULL;
|
||||
g_slist_free(list);
|
||||
return (const char * const *)ret;
|
||||
}
|
||||
//static void * mem_from_g_alloc(void * mem, size_t size)
|
||||
//{
|
||||
// if (g_mem_is_system_malloc()) return mem;
|
||||
//
|
||||
// if (!size) size=strlen((char*)mem)+1;
|
||||
//
|
||||
// void * ret=malloc(size);
|
||||
// memcpy(ret, mem, size);
|
||||
// g_free(ret);
|
||||
// return ret;
|
||||
//}
|
||||
//
|
||||
////enum mbox_sev { mb_info, mb_warn, mb_err };
|
||||
////enum mbox_btns { mb_ok, mb_okcancel, mb_yesno };
|
||||
//bool window_message_box(const char * text, const char * title, enum mbox_sev severity, enum mbox_btns buttons)
|
||||
//{
|
||||
// //"Please note that GTK_BUTTONS_OK, GTK_BUTTONS_YES_NO and GTK_BUTTONS_OK_CANCEL are discouraged by the GNOME HIG."
|
||||
// //I do not listen to advise without a rationale. Tell me which section it violates, and I'll consider it.
|
||||
// GtkMessageType sev[3]={ GTK_MESSAGE_OTHER, GTK_MESSAGE_WARNING, GTK_MESSAGE_ERROR };
|
||||
// GtkButtonsType btns[3]={ GTK_BUTTONS_OK, GTK_BUTTONS_OK_CANCEL, GTK_BUTTONS_YES_NO };
|
||||
// GtkWidget* dialog=gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, sev[severity], btns[buttons], "%s", text);
|
||||
// gint ret=gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
// gtk_widget_destroy(dialog);
|
||||
// return (ret==GTK_RESPONSE_ACCEPT || ret==GTK_RESPONSE_OK || ret==GTK_RESPONSE_YES);
|
||||
//}
|
||||
//
|
||||
//const char * const * window_file_picker(window * parent,
|
||||
// const char * title,
|
||||
// const char * const * extensions,
|
||||
// const char * extdescription,
|
||||
// bool dylib,
|
||||
// bool multiple)
|
||||
//{
|
||||
// static char * * ret=NULL;
|
||||
// if (ret)
|
||||
// {
|
||||
// char * * del=ret;
|
||||
// while (*del)
|
||||
// {
|
||||
// free(*del);
|
||||
// del++;
|
||||
// }
|
||||
// free(ret);
|
||||
// ret=NULL;
|
||||
// }
|
||||
//
|
||||
// GtkFileChooser* dialog=GTK_FILE_CHOOSER(
|
||||
// gtk_file_chooser_dialog_new(
|
||||
// title,
|
||||
// GTK_WINDOW(parent?(void*)parent->_get_handle():NULL),
|
||||
// GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
// "_Cancel",
|
||||
// GTK_RESPONSE_CANCEL,
|
||||
// "_Open",
|
||||
// GTK_RESPONSE_ACCEPT,
|
||||
// NULL));
|
||||
// gtk_file_chooser_set_select_multiple(dialog, multiple);
|
||||
// gtk_file_chooser_set_local_only(dialog, dylib);
|
||||
//
|
||||
// GtkFileFilter* filter;
|
||||
//
|
||||
// if (*extensions)
|
||||
// {
|
||||
// filter=gtk_file_filter_new();
|
||||
// gtk_file_filter_set_name(filter, extdescription);
|
||||
// char extstr[64];
|
||||
// extstr[0]='*';
|
||||
// extstr[1]='.';
|
||||
// while (*extensions)
|
||||
// {
|
||||
// strcpy(extstr+2, *extensions+(**extensions=='.'));
|
||||
// gtk_file_filter_add_pattern(filter, extstr);
|
||||
// extensions++;
|
||||
// }
|
||||
// gtk_file_chooser_add_filter(dialog, filter);
|
||||
// }
|
||||
//
|
||||
// filter=gtk_file_filter_new();
|
||||
// gtk_file_filter_set_name(filter, "All files");
|
||||
// gtk_file_filter_add_pattern(filter, "*");
|
||||
// gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
|
||||
//
|
||||
// if (gtk_dialog_run(GTK_DIALOG(dialog))!=GTK_RESPONSE_ACCEPT)
|
||||
// {
|
||||
// gtk_widget_destroy(GTK_WIDGET(dialog));
|
||||
// return NULL;
|
||||
// }
|
||||
//
|
||||
// GSList * list=gtk_file_chooser_get_uris(dialog);
|
||||
// gtk_widget_destroy(GTK_WIDGET(dialog));
|
||||
// unsigned int listlen=g_slist_length(list);
|
||||
// if (!listlen)
|
||||
// {
|
||||
// g_slist_free(list);
|
||||
// return NULL;
|
||||
// }
|
||||
// ret=malloc(sizeof(char*)*(listlen+1));
|
||||
//
|
||||
// char * * retcopy=ret;
|
||||
// GSList * listcopy=list;
|
||||
// while (listcopy)
|
||||
// {
|
||||
// *retcopy=window_get_absolute_path(NULL, (char*)listcopy->data, true);
|
||||
// g_free(listcopy->data);
|
||||
// retcopy++;
|
||||
// listcopy=listcopy->next;
|
||||
// }
|
||||
// ret[listlen]=NULL;
|
||||
// g_slist_free(list);
|
||||
// return (const char * const *)ret;
|
||||
//}
|
||||
|
||||
void window_run_iter()
|
||||
{
|
||||
@@ -210,6 +210,7 @@ void window_run_wait()
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
char * window_get_absolute_path(const char * basepath, const char * path, bool allow_up)
|
||||
{
|
||||
if (!path) return NULL;
|
||||
@@ -259,11 +260,6 @@ char * window_get_native_path(const char * path)
|
||||
return (char*)mem_from_g_alloc(ret, 0);
|
||||
}
|
||||
|
||||
uint64_t window_get_time()
|
||||
{
|
||||
return g_get_monotonic_time();
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool file_read(const char * filename, void* * data, size_t * len)
|
||||
@@ -356,3 +352,4 @@ void file_find_close(void* find)
|
||||
g_object_unref((GFileEnumerator*)find);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -14,7 +14,7 @@ widget_padding::widget_padding(bool vertical)
|
||||
this->heightprio=(vertical ? 2 : 0);
|
||||
}
|
||||
|
||||
unsigned int widget_padding::init(struct window * parent, uintptr_t parenthandle) { return 0; }
|
||||
unsigned int widget_padding::init(window * parent, uintptr_t parenthandle) { return 0; }
|
||||
void widget_padding::measure() {}
|
||||
void widget_padding::place(void* resizeinf, unsigned int x, unsigned int y, unsigned int width, unsigned int height) {}
|
||||
|
||||
@@ -46,8 +46,8 @@ void widget_layout::construct(unsigned int numchildren, widget_base* * children,
|
||||
m->totsize[0]=totwidth;
|
||||
m->totsize[1]=totheight;
|
||||
|
||||
m->children=malloc(sizeof(struct widget_base*)*numchildren);
|
||||
memcpy(m->children, children, sizeof(struct widget_base*)*numchildren);
|
||||
m->children=malloc(sizeof(widget_base*)*numchildren);
|
||||
memcpy(m->children, children, sizeof(widget_base*)*numchildren);
|
||||
|
||||
for (unsigned int dir=0;dir<2;dir++)
|
||||
{
|
||||
@@ -96,7 +96,7 @@ widget_layout::~widget_layout()
|
||||
delete m;
|
||||
}
|
||||
|
||||
unsigned int widget_layout::init(struct window * parent, uintptr_t parenthandle)
|
||||
unsigned int widget_layout::init(window * parent, uintptr_t parenthandle)
|
||||
{
|
||||
unsigned int ret=0;
|
||||
for (unsigned int i=0;i<m->numchildren;i++)
|
||||
@@ -370,28 +370,6 @@ widget_layout* widget_create_radio_group(bool vertical, widget_radio* * leader,
|
||||
return ret;
|
||||
}
|
||||
|
||||
widget_listbox_virtual::widget_listbox_virtual(const char * firstcol, ...)
|
||||
{
|
||||
unsigned int numcols=1;
|
||||
|
||||
va_list args;
|
||||
va_start(args, firstcol);
|
||||
while (va_arg(args, const char*)) numcols++;
|
||||
va_end(args);
|
||||
|
||||
const char * * columns=malloc(sizeof(const char*)*numcols);
|
||||
columns[0]=firstcol;
|
||||
va_start(args, firstcol);
|
||||
for (unsigned int i=1;i<numcols;i++)
|
||||
{
|
||||
columns[i]=va_arg(args, const char*);
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
construct(numcols, columns);
|
||||
free(columns);
|
||||
}
|
||||
|
||||
widget_layout::widget_layout(bool vertical, bool uniform, widget_base* firstchild, ...)
|
||||
{
|
||||
unsigned int numchildren=1;
|
||||
|
||||
@@ -71,7 +71,6 @@ static void measure_text(const char * text, unsigned int * width, unsigned int *
|
||||
if (height) *height=rc.bottom;
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK viewport_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
void _window_init_inner()
|
||||
{
|
||||
//HDC hdc=GetDC(NULL);
|
||||
@@ -104,19 +103,6 @@ void _window_init_inner()
|
||||
//SelectObject(hdc, prevfont);
|
||||
//ReleaseDC(NULL, hdc);
|
||||
|
||||
WNDCLASS wc;
|
||||
wc.style=0;
|
||||
wc.lpfnWndProc=viewport_WindowProc;
|
||||
wc.cbClsExtra=0;
|
||||
wc.cbWndExtra=0;
|
||||
wc.hInstance=GetModuleHandle(NULL);
|
||||
wc.hIcon=LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(1));
|
||||
wc.hCursor=LoadCursor(NULL, IDC_ARROW);
|
||||
wc.hbrBackground=GetSysColorBrush(COLOR_3DFACE);//(HBRUSH)(COLOR_WINDOW + 1);
|
||||
wc.lpszMenuName=NULL;
|
||||
wc.lpszClassName="minir_viewport";
|
||||
RegisterClass(&wc);
|
||||
|
||||
measure_text("XXXXXXXXXXXX", &xwidth, NULL);
|
||||
|
||||
INITCOMMONCONTROLSEX initctrls;
|
||||
@@ -129,14 +115,21 @@ void _window_init_inner()
|
||||
|
||||
static void place_window(HWND hwnd, void* resizeinf, unsigned int x, unsigned int y, unsigned int width, unsigned int height)
|
||||
{
|
||||
HDWP* hdwp=(HDWP*)resizeinf;
|
||||
*hdwp=DeferWindowPos(*hdwp, hwnd, NULL, x, y, width, height, SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOOWNERZORDER|SWP_NOZORDER);
|
||||
if (resizeinf)
|
||||
{
|
||||
HDWP* hdwp = (HDWP*)resizeinf;
|
||||
*hdwp = DeferWindowPos(*hdwp, hwnd, NULL, x, y, width, height, SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOOWNERZORDER|SWP_NOZORDER);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetWindowPos(hwnd, NULL, x, y, width, height, SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOOWNERZORDER|SWP_NOZORDER);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct widget_label::impl {
|
||||
struct window * parent;
|
||||
window* parent;
|
||||
HWND hwnd;
|
||||
uint8_t state;
|
||||
//char padding[7];
|
||||
@@ -153,7 +146,7 @@ widget_label::widget_label(const char * text) : m(new impl)
|
||||
m->state=0;
|
||||
}
|
||||
|
||||
unsigned int widget_label::init(struct window * parent, uintptr_t parenthandle)
|
||||
unsigned int widget_label::init(window* parent, uintptr_t parenthandle)
|
||||
{
|
||||
m->parent=parent;
|
||||
char* text=(char*)m->hwnd;
|
||||
@@ -210,7 +203,7 @@ widget_label* widget_label::set_alignment(int alignment)
|
||||
|
||||
|
||||
struct widget_button::impl {
|
||||
//struct window * parent;
|
||||
//window* parent;
|
||||
HWND hwnd;
|
||||
|
||||
function<void()> onclick;
|
||||
@@ -232,7 +225,7 @@ widget_button::widget_button(const char * text) : m(new impl)
|
||||
m->hwnd=(HWND)strdup(text);
|
||||
}
|
||||
|
||||
unsigned int widget_button::init(struct window * parent, uintptr_t parenthandle)
|
||||
unsigned int widget_button::init(window* parent, uintptr_t parenthandle)
|
||||
{
|
||||
char* text=(char*)m->hwnd;
|
||||
m->hwnd=CreateWindow(WC_BUTTON, text, WS_CHILD|WS_VISIBLE|WS_TABSTOP, 0, 0, 16, 16,
|
||||
@@ -280,7 +273,7 @@ widget_button* widget_button::set_onclick(function<void()> onclick)
|
||||
|
||||
|
||||
struct widget_checkbox::impl {
|
||||
struct window * parent;
|
||||
window* parent;
|
||||
HWND hwnd;
|
||||
|
||||
function<void(bool checked)> onclick;
|
||||
@@ -302,7 +295,7 @@ widget_checkbox::widget_checkbox(const char * text) : m(new impl)
|
||||
m->hwnd=(HWND)strdup(text);
|
||||
}
|
||||
|
||||
unsigned int widget_checkbox::init(struct window * parent, uintptr_t parenthandle)
|
||||
unsigned int widget_checkbox::init(window* parent, uintptr_t parenthandle)
|
||||
{
|
||||
m->parent=parent;
|
||||
char* text=(char*)m->hwnd;
|
||||
@@ -386,7 +379,7 @@ struct widget_radio::impl {
|
||||
bool disabled;
|
||||
};
|
||||
struct { //active otherwise
|
||||
struct window * parent;
|
||||
window* parent;
|
||||
HWND hwnd;
|
||||
};
|
||||
};
|
||||
@@ -433,7 +426,7 @@ widget_radio::widget_radio(const char * text) : m(new impl)
|
||||
m->text=strdup(text);
|
||||
}
|
||||
|
||||
unsigned int widget_radio::init(struct window * parent, uintptr_t parenthandle)
|
||||
unsigned int widget_radio::init(window* parent, uintptr_t parenthandle)
|
||||
{
|
||||
bool disabled=m->disabled;
|
||||
char* text=m->text;
|
||||
@@ -728,25 +721,34 @@ struct widget_canvas_win32;
|
||||
|
||||
|
||||
struct widget_viewport::impl {
|
||||
struct window * parent;
|
||||
window* parent;
|
||||
HWND hwnd;
|
||||
|
||||
bool hide_cursor_user;
|
||||
bool hide_cursor_timer;
|
||||
LPARAM lastmousepos;
|
||||
//bool hide_cursor_user;
|
||||
//bool hide_cursor_timer;
|
||||
//LPARAM lastmousepos;
|
||||
|
||||
function<void(const char * const * filenames)> on_file_drop;
|
||||
//function<void(const char * const * filenames)> on_file_drop;
|
||||
|
||||
unsigned int lastx;
|
||||
unsigned int lasty;
|
||||
unsigned int lastwidth;
|
||||
unsigned int lastheight;
|
||||
|
||||
function<void(size_t width, size_t height)> onresize;
|
||||
function<void()> ondestroy;
|
||||
};
|
||||
|
||||
|
||||
|
||||
unsigned int widget_viewport::init(struct window * parent, uintptr_t parenthandle)
|
||||
unsigned int widget_viewport::init(window* parent, uintptr_t parenthandle)
|
||||
{
|
||||
m->parent=parent;
|
||||
m->hwnd=CreateWindow("minir_viewport", "", WS_CHILD|WS_VISIBLE, 0, 0, 16, 16, // TODO: figure out why this isn't resized properly
|
||||
(HWND)parenthandle, NULL, GetModuleHandle(NULL), NULL); // probably a missed reflow
|
||||
m->parent = parent;
|
||||
m->hwnd = NULL;
|
||||
//CreateWindow("arlib_viewport", "", WS_CHILD|WS_VISIBLE, 0, 0, 16, 16, // TODO: figure out why this isn't resized properly
|
||||
// (HWND)parenthandle, NULL, GetModuleHandle(NULL), NULL); // probably a missed reflow
|
||||
SetWindowLongPtr(m->hwnd, GWLP_USERDATA, (LONG_PTR)this);
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
widget_viewport::widget_viewport(unsigned int width, unsigned int height) : m(new impl)
|
||||
@@ -754,130 +756,159 @@ widget_viewport::widget_viewport(unsigned int width, unsigned int height) : m(ne
|
||||
this->widthprio=0;
|
||||
this->heightprio=0;
|
||||
|
||||
this->width=width;
|
||||
this->height=height;
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
//no padding here
|
||||
|
||||
m->hide_cursor_user=false;
|
||||
m->hide_cursor_timer=true;
|
||||
m->lastmousepos=-1;//random value, just so Valgrind-like tools won't throw.
|
||||
m->on_file_drop=NULL;
|
||||
//m->hide_cursor_user=false;
|
||||
//m->hide_cursor_timer=true;
|
||||
//m->lastmousepos=-1;//random value, just so Valgrind-like tools won't throw.
|
||||
//m->on_file_drop=NULL;
|
||||
}
|
||||
|
||||
void widget_viewport::measure() {}
|
||||
|
||||
void widget_viewport::place(void* resizeinf, unsigned int x, unsigned int y, unsigned int width, unsigned int height)
|
||||
{
|
||||
place_window(m->hwnd, resizeinf, x, y, width, height);
|
||||
m->lastx = x;
|
||||
m->lasty = y;
|
||||
if (m->hwnd)
|
||||
{
|
||||
//intentionally doesn't pass resizeinf, onresize() requires the widget resized before being called
|
||||
//not sure if I need DeferWindowPos at all, doubt it saves any time on modern systems
|
||||
place_window(m->hwnd, NULL, x, y, width, height);
|
||||
}
|
||||
if (width != m->lastwidth || height != m->lastheight)
|
||||
{
|
||||
m->lastwidth = width;
|
||||
m->lastheight = height;
|
||||
m->onresize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
widget_viewport::~widget_viewport()
|
||||
{
|
||||
m->ondestroy();
|
||||
delete m;
|
||||
}
|
||||
|
||||
widget_viewport* widget_viewport::resize(unsigned int width, unsigned int height)
|
||||
{
|
||||
this->width=width;
|
||||
this->height=height;
|
||||
if (!m->parent->_reflow())
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
if (m->parent->is_visible())
|
||||
{
|
||||
SetWindowPos(m->hwnd, NULL, 0, 0, width, height, SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
|
||||
m->parent->_reflow();
|
||||
}
|
||||
else
|
||||
{
|
||||
//video drivers may be sensitive to size of hidden windows, so make sure it's resized
|
||||
place(NULL, 0, 0, width, height);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
uintptr_t widget_viewport::get_window_handle()
|
||||
uintptr_t widget_viewport::get_parent()
|
||||
{
|
||||
return (uintptr_t)m->hwnd;
|
||||
return m->parent->_get_handle();
|
||||
}
|
||||
|
||||
widget_viewport* widget_viewport::set_hide_cursor(bool hide)
|
||||
void widget_viewport::set_child(uintptr_t windowhandle, function<void(size_t width, size_t height)> onresize, function<void()> ondestroy)
|
||||
{
|
||||
m->hide_cursor_user=hide;
|
||||
if (m->hide_cursor_user && m->hide_cursor_timer) SetCursor(NULL);
|
||||
else SetCursor(LoadCursor(NULL, IDC_ARROW));
|
||||
m->hwnd = (HWND)windowhandle;
|
||||
m->onresize = onresize;
|
||||
m->ondestroy = ondestroy;
|
||||
|
||||
return this;
|
||||
m->lastwidth = -1; // force onresize() to be called
|
||||
place(NULL, m->lastx, m->lasty, this->width, this->height);
|
||||
}
|
||||
|
||||
widget_viewport* widget_viewport::set_support_drop(function<void(const char * const * filenames)> on_file_drop)
|
||||
{
|
||||
DragAcceptFiles(m->hwnd, TRUE);
|
||||
m->on_file_drop=on_file_drop;
|
||||
|
||||
return this;
|
||||
}
|
||||
//widget_viewport* widget_viewport::set_hide_cursor(bool hide)
|
||||
//{
|
||||
// m->hide_cursor_user=hide;
|
||||
// if (m->hide_cursor_user && m->hide_cursor_timer) SetCursor(NULL);
|
||||
// else SetCursor(LoadCursor(NULL, IDC_ARROW));
|
||||
//
|
||||
// return this;
|
||||
//}
|
||||
//
|
||||
//widget_viewport* widget_viewport::set_support_drop(function<void(const char * const * filenames)> on_file_drop)
|
||||
//{
|
||||
// DragAcceptFiles(m->hwnd, TRUE);
|
||||
// m->on_file_drop=on_file_drop;
|
||||
//
|
||||
// return this;
|
||||
//}
|
||||
|
||||
static LRESULT CALLBACK viewport_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
widget_viewport* obj=(widget_viewport*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_MOUSEMOVE:
|
||||
{
|
||||
//windows, what exactly is the point behind the bogus mouse move messages
|
||||
//you clearly know they exist (http://blogs.msdn.com/b/oldnewthing/archive/2009/06/17/9763416.aspx), why not block them?
|
||||
if (lParam==obj->m->lastmousepos) break;
|
||||
obj->m->lastmousepos=lParam;
|
||||
|
||||
SetTimer(obj->m->hwnd, TIMER_MOUSEHIDE, 1000, NULL);
|
||||
TRACKMOUSEEVENT tme={ sizeof(tme), TME_LEAVE, obj->m->hwnd, HOVER_DEFAULT };
|
||||
TrackMouseEvent(&tme);
|
||||
obj->m->hide_cursor_timer=false;
|
||||
}
|
||||
break;
|
||||
case WM_MOUSELEAVE:
|
||||
case WM_NCMOUSEMOVE:
|
||||
{
|
||||
KillTimer(obj->m->hwnd, TIMER_MOUSEHIDE);
|
||||
obj->m->hide_cursor_timer=false;
|
||||
}
|
||||
break;
|
||||
case WM_TIMER:
|
||||
{
|
||||
if (wParam==TIMER_MOUSEHIDE)
|
||||
{
|
||||
obj->m->hide_cursor_timer=true;
|
||||
if (obj->m->hide_cursor_user) SetCursor(NULL);
|
||||
KillTimer(obj->m->hwnd, TIMER_MOUSEHIDE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WM_SETCURSOR:
|
||||
{
|
||||
if (obj->m->hide_cursor_user && obj->m->hide_cursor_timer) SetCursor(NULL);
|
||||
else goto _default;
|
||||
}
|
||||
break;
|
||||
case WM_DROPFILES:
|
||||
{
|
||||
HDROP hdrop=(HDROP)wParam;
|
||||
UINT numfiles=DragQueryFile(hdrop, 0xFFFFFFFF, NULL, 0);//but what if I drop four billion files?
|
||||
char * * filenames=malloc(sizeof(char*)*(numfiles+1));
|
||||
for (UINT i=0;i<numfiles;i++)
|
||||
{
|
||||
UINT len=DragQueryFile(hdrop, i, NULL, 0);
|
||||
filenames[i]=malloc(len+1);
|
||||
DragQueryFile(hdrop, i, filenames[i], len+1);
|
||||
for (unsigned int j=0;filenames[i][j];j++)
|
||||
{
|
||||
if (filenames[i][j]=='\\') filenames[i][j]='/';
|
||||
}
|
||||
}
|
||||
filenames[numfiles]=NULL;
|
||||
DragFinish(hdrop);
|
||||
obj->m->on_file_drop((const char * const *)filenames);
|
||||
for (UINT i=0;i<numfiles;i++) free(filenames[i]);
|
||||
free(filenames);
|
||||
}
|
||||
break;
|
||||
_default:
|
||||
default:
|
||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//static LRESULT CALLBACK viewport_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
//{
|
||||
// widget_viewport* obj=(widget_viewport*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
// switch (uMsg)
|
||||
// {
|
||||
// case WM_MOUSEMOVE:
|
||||
// {
|
||||
// //windows, what exactly is the point behind the bogus mouse move messages
|
||||
// //you clearly know they exist (http://blogs.msdn.com/b/oldnewthing/archive/2009/06/17/9763416.aspx), why not block them?
|
||||
// if (lParam==obj->m->lastmousepos) break;
|
||||
// obj->m->lastmousepos=lParam;
|
||||
//
|
||||
// SetTimer(obj->m->hwnd, TIMER_MOUSEHIDE, 1000, NULL);
|
||||
// TRACKMOUSEEVENT tme={ sizeof(tme), TME_LEAVE, obj->m->hwnd, HOVER_DEFAULT };
|
||||
// TrackMouseEvent(&tme);
|
||||
// obj->m->hide_cursor_timer=false;
|
||||
// }
|
||||
// break;
|
||||
// case WM_MOUSELEAVE:
|
||||
// case WM_NCMOUSEMOVE:
|
||||
// {
|
||||
// KillTimer(obj->m->hwnd, TIMER_MOUSEHIDE);
|
||||
// obj->m->hide_cursor_timer=false;
|
||||
// }
|
||||
// break;
|
||||
// case WM_TIMER:
|
||||
// {
|
||||
// if (wParam==TIMER_MOUSEHIDE)
|
||||
// {
|
||||
// obj->m->hide_cursor_timer=true;
|
||||
// if (obj->m->hide_cursor_user) SetCursor(NULL);
|
||||
// KillTimer(obj->m->hwnd, TIMER_MOUSEHIDE);
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
// case WM_SETCURSOR:
|
||||
// {
|
||||
// if (obj->m->hide_cursor_user && obj->m->hide_cursor_timer) SetCursor(NULL);
|
||||
// else goto _default;
|
||||
// }
|
||||
// break;
|
||||
// case WM_DROPFILES:
|
||||
// {
|
||||
// HDROP hdrop=(HDROP)wParam;
|
||||
// UINT numfiles=DragQueryFile(hdrop, 0xFFFFFFFF, NULL, 0);//but what if I drop four billion files?
|
||||
// char * * filenames=malloc(sizeof(char*)*(numfiles+1));
|
||||
// for (UINT i=0;i<numfiles;i++)
|
||||
// {
|
||||
// UINT len=DragQueryFile(hdrop, i, NULL, 0);
|
||||
// filenames[i]=malloc(len+1);
|
||||
// DragQueryFile(hdrop, i, filenames[i], len+1);
|
||||
// for (unsigned int j=0;filenames[i][j];j++)
|
||||
// {
|
||||
// if (filenames[i][j]=='\\') filenames[i][j]='/';
|
||||
// }
|
||||
// }
|
||||
// filenames[numfiles]=NULL;
|
||||
// DragFinish(hdrop);
|
||||
// obj->m->on_file_drop((const char * const *)filenames);
|
||||
// for (UINT i=0;i<numfiles;i++) free(filenames[i]);
|
||||
// free(filenames);
|
||||
// }
|
||||
// break;
|
||||
// _default:
|
||||
// default:
|
||||
// return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
// }
|
||||
// return 0;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
@@ -889,7 +920,7 @@ static LRESULT CALLBACK viewport_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
|
||||
#endif
|
||||
|
||||
struct widget_listbox_virtual::impl {
|
||||
struct window * parent;
|
||||
window* parent;
|
||||
HWND hwnd;
|
||||
|
||||
unsigned int rows;
|
||||
@@ -937,7 +968,7 @@ void widget_listbox_virtual::construct(unsigned int numcolumns, const char * * c
|
||||
m->initialized=false;
|
||||
}
|
||||
|
||||
unsigned int widget_listbox_virtual::init(struct window * parent, uintptr_t parenthandle)
|
||||
unsigned int widget_listbox_virtual::init(window* parent, uintptr_t parenthandle)
|
||||
{
|
||||
m->parent=parent;
|
||||
const char * * columns=(const char**)m->hwnd;
|
||||
@@ -1227,11 +1258,11 @@ struct widget_frame::impl {
|
||||
widget_frame::widget_frame(const char * text, widget_base* contents) : m(new impl)
|
||||
{
|
||||
m->initialized=false;
|
||||
m->child=(struct widget_base*)contents;
|
||||
m->child=contents;
|
||||
m->hwnd=(HWND)strdup(text);
|
||||
}
|
||||
|
||||
unsigned int widget_frame::init(struct window * parent, uintptr_t parenthandle)
|
||||
unsigned int widget_frame::init(window* parent, uintptr_t parenthandle)
|
||||
{
|
||||
//this->parent=parent;//this one can't do anything that changes its size
|
||||
char * text=(char*)m->hwnd;
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include <windows.h>
|
||||
#include <commdlg.h>
|
||||
#define bind bind_func
|
||||
#ifdef ARLIB_WUTF
|
||||
#include "../wutf/wutf.h"
|
||||
#endif
|
||||
|
||||
//Number of ugly hacks: 5
|
||||
//If a status bar item is right-aligned, a space is appended.
|
||||
@@ -59,6 +62,7 @@ void window_init(int * argc, char * * argv[])
|
||||
//QueryPerformanceFrequency(&timer_freq);
|
||||
}
|
||||
|
||||
#if 0
|
||||
file* file::create(const char * filename)
|
||||
{
|
||||
//sorry Windows, no fancy features for you, you suck
|
||||
@@ -73,7 +77,7 @@ bool window_message_box(const char * text, const char * title, enum mbox_sev sev
|
||||
return (ret==IDOK || ret==IDYES);
|
||||
}
|
||||
|
||||
const char * const * window_file_picker(struct window * parent,
|
||||
const char * const * window_file_picker(window * parent,
|
||||
const char * title,
|
||||
const char * const * extensions,
|
||||
const char * extdescription,
|
||||
@@ -178,19 +182,6 @@ char * window_get_absolute_path(const char * basepath, const char * path, bool a
|
||||
return _window_native_get_absolute_path(basepath, path, allow_up);
|
||||
}
|
||||
|
||||
uint64_t window_get_time()
|
||||
{
|
||||
//this one has an accuracy of 10ms by default
|
||||
ULARGE_INTEGER time;
|
||||
GetSystemTimeAsFileTime((LPFILETIME)&time);
|
||||
return time.QuadPart/10;//this one is in intervals of 100 nanoseconds, for some insane reason. We want microseconds.
|
||||
|
||||
//this one is slow - ~800fps -> ~500fps if called each frame
|
||||
//LARGE_INTEGER timer_now;
|
||||
//QueryPerformanceCounter(&timer_now);
|
||||
//return timer_now.QuadPart/timer_freq.QuadPart;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool file_read(const char * filename, void* * data, size_t * len)
|
||||
@@ -297,3 +288,4 @@ void file_find_close(void* find_)
|
||||
free(find);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -27,7 +27,7 @@ void _window_init_shell()
|
||||
wc.hCursor=LoadCursor(NULL, IDC_ARROW);
|
||||
wc.hbrBackground=GetSysColorBrush(COLOR_3DFACE);
|
||||
wc.lpszMenuName=NULL;
|
||||
wc.lpszClassName="minir";
|
||||
wc.lpszClassName="arlib";
|
||||
RegisterClass(&wc);
|
||||
|
||||
//DWORD version=GetVersion();
|
||||
@@ -36,7 +36,7 @@ void _window_init_shell()
|
||||
}
|
||||
|
||||
static HMENU menu_to_hmenu(windowmenu_menu* menu);
|
||||
//static void menu_delete(struct windowmenu_win32 * This);
|
||||
//static void menu_delete(windowmenu_win32 * This);
|
||||
static void menu_activate(HMENU menu, DWORD pos);
|
||||
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
@@ -44,20 +44,21 @@ namespace {
|
||||
#define WS_BASE WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX // okay microsoft, did I miss anything?
|
||||
#define WS_RESIZABLE (WS_BASE|WS_MAXIMIZEBOX|WS_THICKFRAME)
|
||||
#define WS_NONRESIZ (WS_BASE|WS_BORDER)
|
||||
//static bool _reflow(struct window * this_);
|
||||
//static void reflow_force(struct window_win32 * this_);
|
||||
//static bool _reflow(window * this_);
|
||||
//static void reflow_force(window_win32 * this_);
|
||||
|
||||
static HWND activedialog;
|
||||
|
||||
static struct window_win32 * firstwindow;
|
||||
static struct window_win32 * modalwindow;
|
||||
class window_win32;
|
||||
static window_win32 * firstwindow;
|
||||
static window_win32 * modalwindow;
|
||||
|
||||
class window_win32 : public window {
|
||||
public:
|
||||
|
||||
//used by modality
|
||||
struct window_win32 * prev;
|
||||
struct window_win32 * next;
|
||||
window_win32 * prev;
|
||||
window_win32 * next;
|
||||
bool modal;
|
||||
//char padding[7];
|
||||
|
||||
@@ -114,7 +115,7 @@ function<bool()> onclose;
|
||||
{
|
||||
RECT rect;
|
||||
SendMessage(this->status, SB_GETRECT, 0, (LPARAM)&rect);
|
||||
this->status_resizegrip_width=rect.bottom-rect.top-8;//assume the size grip has the same width as height
|
||||
this->status_resizegrip_width = (uint8_t)(rect.bottom - rect.top - 8);//assume the size grip has the same width as height
|
||||
}
|
||||
statuswidth-=this->status_resizegrip_width;
|
||||
}
|
||||
@@ -134,9 +135,9 @@ void set_is_dialog()
|
||||
this->isdialog=true;
|
||||
}
|
||||
|
||||
void set_parent(struct window * parent_)
|
||||
void set_parent(window * parent_)
|
||||
{
|
||||
struct window_win32 * parent=(struct window_win32*)parent_;
|
||||
window_win32 * parent=(window_win32*)parent_;
|
||||
SetWindowLongPtr(this->hwnd, GWLP_HWNDPARENT, (LONG_PTR)parent->hwnd);
|
||||
}
|
||||
|
||||
@@ -147,7 +148,7 @@ void set_parent(struct window * parent_)
|
||||
//disable all windows
|
||||
if (!modalwindow)//except if they're already disabled because that's a waste of time.
|
||||
{
|
||||
struct window_win32 * wndw=firstwindow;
|
||||
window_win32 * wndw=firstwindow;
|
||||
while (wndw)
|
||||
{
|
||||
if (wndw!=this) EnableWindow(wndw->hwnd, false);
|
||||
@@ -161,7 +162,7 @@ void set_parent(struct window * parent_)
|
||||
//we're gone now - if we're the one holding the windows locked, enable them
|
||||
if (this == modalwindow)
|
||||
{
|
||||
struct window_win32 * wndw=firstwindow;
|
||||
window_win32 * wndw=firstwindow;
|
||||
while (wndw)
|
||||
{
|
||||
EnableWindow(wndw->hwnd, true);
|
||||
@@ -449,15 +450,15 @@ window_win32(widget_base* contents)
|
||||
if (this->next) this->next->prev=this;
|
||||
firstwindow=this;
|
||||
|
||||
this->contents=(struct widget_base*)contents;
|
||||
this->contents=(widget_base*)contents;
|
||||
this->contents->measure();
|
||||
//the 6 and 28 are arbitrary; we'll set ourselves to a better size later. Windows' default placement algorithm sucks, anyways.
|
||||
//const char * xpmsg="Do not submit bug reports. Windows XP is unsupported by Microsoft, and unsupported by me.";
|
||||
this->hwnd=CreateWindow("minir", /*isxp?xpmsg:*/"", WS_NONRESIZ, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
this->hwnd=CreateWindow("arlib", /*isxp?xpmsg:*/"", WS_NONRESIZ, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
this->contents->width+6, this->contents->height+28, NULL, NULL, GetModuleHandle(NULL), NULL);
|
||||
SetWindowLongPtr(this->hwnd, GWLP_USERDATA, (LONG_PTR)this);
|
||||
SetWindowLongPtr(this->hwnd, GWLP_WNDPROC, (LONG_PTR)WindowProc);
|
||||
this->numchildwin = this->contents->init((struct window*)this, (uintptr_t)this->hwnd);
|
||||
this->numchildwin = this->contents->init((window*)this, (uintptr_t)this->hwnd);
|
||||
|
||||
this->status=NULL;
|
||||
this->menu=NULL;
|
||||
@@ -482,7 +483,7 @@ window* window_create(widget_base* contents)
|
||||
|
||||
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
struct window_win32 * This=(struct window_win32*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
window_win32 * This=(window_win32*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_CTLCOLOREDIT: return _window_get_widget_color(uMsg, (HWND)lParam, (HDC)wParam, hwnd);
|
||||
@@ -527,7 +528,7 @@ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM l
|
||||
//if (This->delayfree==2)
|
||||
//{
|
||||
// This->delayfree=0;
|
||||
// free_((struct window*)This);
|
||||
// free_((window*)This);
|
||||
// break;
|
||||
//}
|
||||
//This->delayfree=0;
|
||||
|
||||
@@ -58,24 +58,24 @@ public:
|
||||
virtual void set_onclose(function<bool()> onclose) = 0;
|
||||
|
||||
//Appends a menu bar to the top of the window. If the window has a menu already, it's replaced. NULL removes the menu.
|
||||
//There's no real reason to replace it, though. Just change it.
|
||||
//But there's no real reason to replace it. Just change it.
|
||||
//Must be created by windowmenu_menu::create_top.
|
||||
virtual void set_menu(windowmenu_menu* menu) = 0;
|
||||
|
||||
//Creates a status bar at the bottom of the window. It is undefined what happens if numslots equals or exceeds 32.
|
||||
//align is how each string is aligned; 0 means touch the left side, 1 means centered, 2 means touch the right side.
|
||||
//dividerpos is in 240ths of the window size. Values 0 and 240, as well as
|
||||
// a divider position to the left of the previous one, yield undefined behaviour.
|
||||
//dividerpos[numslots-1] is ignored; the status bar always covers the entire width of the window.
|
||||
//It is implementation defined whether the previous status bar strings remain, or if you must use statusbar_set again.
|
||||
//It is implementation defined whether dividers will be drawn. However, it is guaranteed
|
||||
// that the implementation will look like the rest of the operating system, as far as that's feasible.
|
||||
//It is implementation defined what exactly happens if a string is too
|
||||
// long to fit; however, it is guaranteed to show as much as it can.
|
||||
//To remove the status bar, set numslots to 0.
|
||||
virtual void statusbar_create(int numslots, const int * align, const int * dividerpos) = 0;
|
||||
//Sets a string on the status bar. The index is zero-based. All strings are initially blank.
|
||||
virtual void statusbar_set(int slot, const char * text) = 0;
|
||||
////Creates a status bar at the bottom of the window. It is undefined what happens if numslots equals or exceeds 32.
|
||||
////align is how each string is aligned; 0 means touch the left side, 1 means centered, 2 means touch the right side.
|
||||
////dividerpos is in 240ths of the window size. Values 0 and 240, as well as
|
||||
//// a divider position to the left of the previous one, yield undefined behaviour.
|
||||
////dividerpos[numslots-1] is ignored; the status bar always covers the entire width of the window.
|
||||
////It is implementation defined whether the previous status bar strings remain, or if you must use statusbar_set again.
|
||||
////It is implementation defined whether dividers will be drawn. However, it is guaranteed
|
||||
//// that the implementation will look like the rest of the operating system, as far as that's feasible.
|
||||
////It is implementation defined what exactly happens if a string is too
|
||||
//// long to fit; however, it is guaranteed to show as much as it can.
|
||||
////To remove the status bar, set numslots to 0.
|
||||
//virtual void statusbar_create(int numslots, const int * align, const int * dividerpos) = 0;
|
||||
////Sets a string on the status bar. The index is zero-based. All strings are initially blank.
|
||||
//virtual void statusbar_set(int slot, const char * text) = 0;
|
||||
|
||||
//This replaces the contents of a window.
|
||||
virtual void replace_contents(widget_base* contents) = 0;
|
||||
@@ -97,10 +97,11 @@ public:
|
||||
virtual ~window() = 0;
|
||||
|
||||
|
||||
//Returns a native handle to the window. It is implementation defined what this native handle is, or if it's implemented at all.
|
||||
//Only usable by the implementation, don't call them yourself. Not guaranteed to be implemented at all.
|
||||
//Returns a native handle to the window.
|
||||
virtual uintptr_t _get_handle() { return 0; }
|
||||
//Repositions the window contents. May not necessarily be implemented, if reflow requests are detected in other ways.
|
||||
//If false, the reflow will be done later and the old sizes are still present.
|
||||
//Recomputes the window content layout.
|
||||
//If return value is false, the reflow will be done later and the old sizes are still present.
|
||||
virtual bool _reflow() { return false; };
|
||||
};
|
||||
inline window::~window(){}
|
||||
@@ -126,7 +127,7 @@ public:
|
||||
// unless the widget wants the events. (For example, a button will want mouse events, but not file drop events.)
|
||||
//The window handles passed around are implementation defined.
|
||||
//The return value from init() is the number of child windows involved, from the window manager's point of view.
|
||||
virtual unsigned int init(struct window * parent, uintptr_t parenthandle) = 0;
|
||||
virtual unsigned int init(window * parent, uintptr_t parenthandle) = 0;
|
||||
virtual void measure() = 0;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
@@ -134,7 +135,7 @@ public:
|
||||
|
||||
//this one acts roughly like Q_OBJECT
|
||||
#define WIDGET_BASE \
|
||||
unsigned int init(struct window * parent, uintptr_t parenthandle); \
|
||||
unsigned int init(window * parent, uintptr_t parenthandle); \
|
||||
void measure(); \
|
||||
void place(void* resizeinf, unsigned int x, unsigned int y, unsigned int width, unsigned int height);
|
||||
#else
|
||||
@@ -147,8 +148,8 @@ public:
|
||||
//2 - Widget has orders to consume extra space if there's any left over and nothing really wants it. (Padding)
|
||||
//3 - Widget will look better if given extra space. (Textbox, listbox)
|
||||
//4 - Widget is ordered to be resizable. (Canvas, viewport)
|
||||
unsigned char widthprio;
|
||||
unsigned char heightprio;
|
||||
uint8_t widthprio;
|
||||
uint8_t heightprio;
|
||||
virtual ~widget_base() {};
|
||||
};
|
||||
|
||||
@@ -340,31 +341,32 @@ public:
|
||||
#define widget_create_textbox(...) (new widget_textbox(__VA_ARGS__))
|
||||
|
||||
|
||||
//A canvas is a simple image. It's easy to work with, but performance is poor and it can't vsync, so it shouldn't be used for video.
|
||||
class widget_canvas : public widget_base { WIDGET_BASE
|
||||
public:
|
||||
widget_canvas(unsigned int width, unsigned int height);
|
||||
~widget_canvas();
|
||||
//can't disable this
|
||||
|
||||
widget_canvas* resize(unsigned int width, unsigned int height);
|
||||
uint32_t * (*draw_begin)();
|
||||
void draw_end();
|
||||
|
||||
//Whether to hide the cursor while it's on top of this widget.
|
||||
//The mouse won't instantly hide; if it's moving, it will be visible. The exact details are up to the implementation,
|
||||
// but it will be similar to "the mouse is visible if it has moved within the last 1000 milliseconds".
|
||||
widget_canvas* set_hide_cursor(bool hide);
|
||||
|
||||
//This must be called before the window is shown, and only exactly once.
|
||||
//All given filenames are invalidated once the callback returns.
|
||||
widget_canvas* set_support_drop(function<void(const char * const * filenames)> on_file_drop);
|
||||
|
||||
public:
|
||||
struct impl;
|
||||
impl * m;
|
||||
};
|
||||
#define widget_create_canvas(width, height) (new widget_canvas(width, height))
|
||||
////A canvas is a simple image. It's easy to work with, but performance is poor and it can't vsync, so it shouldn't be used for video.
|
||||
//class widget_canvas : public widget_base { WIDGET_BASE
|
||||
//public:
|
||||
// widget_canvas(unsigned int width, unsigned int height);
|
||||
// ~widget_canvas();
|
||||
// //can't disable this
|
||||
//
|
||||
// widget_canvas* resize(unsigned int width, unsigned int height);
|
||||
// uint32_t * (*draw_begin)();
|
||||
// void draw_end();
|
||||
//
|
||||
// //TODO
|
||||
// ////Whether to hide the cursor while it's on top of this widget.
|
||||
// ////The mouse won't instantly hide; if it's moving, it will be visible. The exact details are up to the implementation,
|
||||
// //// but it will be similar to "the mouse is visible if it has moved within the last 1000 milliseconds".
|
||||
// //widget_canvas* set_hide_cursor(bool hide);
|
||||
// //
|
||||
// ////This must be called before the window is shown, and only exactly once.
|
||||
// ////All given filenames are invalidated once the callback returns.
|
||||
// //widget_canvas* set_support_drop(function<void(const char * const * filenames)> on_file_drop);
|
||||
//
|
||||
//public:
|
||||
// struct impl;
|
||||
// impl * m;
|
||||
//};
|
||||
//#define widget_create_canvas(width, height) (new widget_canvas(width, height))
|
||||
|
||||
|
||||
//A viewport fills the same purpose as a canvas, but the tradeoffs go the opposite way.
|
||||
@@ -373,18 +375,35 @@ public:
|
||||
widget_viewport(unsigned int width, unsigned int height);
|
||||
~widget_viewport();
|
||||
|
||||
//can't disable this
|
||||
widget_viewport* resize(unsigned int width, unsigned int height);
|
||||
uintptr_t get_window_handle();
|
||||
//The position is relative to the desktop.
|
||||
void get_position(int * x, int * y, unsigned int * width, unsigned int * height);
|
||||
|
||||
//See documentation of canvas for these.
|
||||
widget_viewport* set_hide_cursor(bool hide);
|
||||
widget_viewport* set_support_drop(function<void(const char * const * filenames)> on_file_drop);
|
||||
//There's no single way to render high-performance graphics (especially 3d), so a separate video driver is required.
|
||||
//Many video drivers (especially OpenGL-based ones) can't render to arbitrary windows, but must create their own windows;
|
||||
// therefore, this widget doesn't create its own window, but expects to be given one by the video driver.
|
||||
// The driver retains ownership and is expected to delete it.
|
||||
//The driver's created window should be a child of this one:
|
||||
uintptr_t get_parent();
|
||||
|
||||
//Keycodes are from libretro; 0 if unknown. Scancodes are implementation defined, but if there is no libretro translation, then none is returned.
|
||||
//widget_viewport* set_kb_callback)(function<void(unsigned int keycode, unsigned int scancode)> keyboard_cb);
|
||||
//As this widget is resizable, it needs a way to report size changes.
|
||||
//This is done via this function. Call it and the widget will move and resize the window to whereever this widget is located.
|
||||
//If the widget changes size, this will be reported to onresize(). Guaranteed to only be called if actually changed.
|
||||
//This callback will be called if the widget is altered by resize(). The rest of Arlib avoids calling callbacks for API-sourced calls,
|
||||
// but the video driver isn't the one who called resize().
|
||||
//The driver must return the new window after the resize. This may be the same as the old one. If different,
|
||||
// the driver is responsible for destroying the old one.
|
||||
//If the program wants to destroy the video driver, it must call set_contents(0, NULL, NULL) before doing so.
|
||||
//If the driver doesn't need the resize callback, it may return 0. However, set_contents() is still required.
|
||||
//ondestroy is called whenever the viewport is destroyed, if the viewport isn't disconnected first.
|
||||
void set_child(uintptr_t windowhandle, function<void(unsigned int width, unsigned int height)> onresize, function<void()> ondestroy);
|
||||
|
||||
//TODO
|
||||
////See documentation of canvas for these.
|
||||
//widget_viewport* set_hide_cursor(bool hide);
|
||||
//widget_viewport* set_support_drop(function<void(const char * const * filenames)> on_file_drop);
|
||||
|
||||
//TODO
|
||||
////Keycodes are from libretro; 0 if unknown. Scancodes are implementation defined and always present.
|
||||
//widget_viewport* set_kb_callback(function<void(unsigned int keycode, unsigned int scancode)> keyboard_cb);
|
||||
|
||||
public:
|
||||
struct impl;
|
||||
@@ -395,7 +414,6 @@ public:
|
||||
|
||||
class widget_listbox_virtual : public widget_base { WIDGET_BASE
|
||||
private:
|
||||
widget_listbox_virtual() {}
|
||||
void construct(unsigned int numcolumns, const char * * columns);
|
||||
|
||||
public:
|
||||
@@ -421,14 +439,14 @@ public:
|
||||
|
||||
//On Windows, the limit is 100 million; if more than that, it puts in 0.
|
||||
// Probably because it's a nice round number, and the listbox row height (19) times 100 million is fairly close to 2^31.
|
||||
//On GTK+, it's 100000; it's slow on huge lists.
|
||||
//TODO: figure out why.
|
||||
//On GTK+, it's 100000; it's slow on huge lists, since it allocates memory for each row,
|
||||
// even when using gtk_tree_view_set_fixed_height_mode and similar.
|
||||
static size_t get_max_rows();
|
||||
|
||||
//If more than get_max_rows(), it's capped to that.
|
||||
widget_listbox_virtual* set_num_rows(size_t rows);
|
||||
|
||||
//Call this after changing anything. It's fine to change multiple rows at once with only one call.
|
||||
//Call this after changing anything. It's fine to change multiple rows before calling this.
|
||||
widget_listbox_virtual* refresh();
|
||||
|
||||
//If the active row changes, set_focus_change will fire. However, onactivate will likely not.
|
||||
@@ -462,7 +480,7 @@ public:
|
||||
#define widget_create_listbox_virtual(...) (new widget_listbox_virtual(__VA_ARGS__))
|
||||
|
||||
|
||||
//If performance is bad, switch to the virtual listbox.
|
||||
//Easier to use than the virtual listbox, but slower. Should be preferred for most usecases.
|
||||
class widget_listbox : public widget_listbox_virtual
|
||||
{
|
||||
size_t numcols;
|
||||
@@ -691,49 +709,46 @@ public:
|
||||
//Tells the window manager to handle recent events and fire whatever callbacks are relevant.
|
||||
//Neither of them are allowed while inside any callback of any kind.
|
||||
//Some other functions may call these two.
|
||||
void window_run_iter();//Returns as soon as possible. Use if you're synchronizing on something else.
|
||||
void window_run_iter();//Returns as soon as possible. Use if, for example, you're displaying an animation.
|
||||
void window_run_wait();//Returns only after doing something. Use while idling. It will return if any
|
||||
// state (other than the time) has changed or if any callback has fired.
|
||||
// It may also return due to uninteresting events, as often as it wants;
|
||||
//It may also return due to uninteresting events, as often as it wants;
|
||||
// however, repeatedly calling it will leave the CPU mostly idle.
|
||||
|
||||
//Shows a message box. You can do that by creating a label and some buttons, but it gives inferior results.
|
||||
//Returns true for OK and Yes, and false for Cancel/No/close window.
|
||||
//The title may or may not be ignored.
|
||||
enum mbox_sev { mb_info, mb_warn, mb_err };
|
||||
enum mbox_btns { mb_ok, mb_okcancel, mb_yesno };
|
||||
bool window_message_box(const char * text, const char * title, enum mbox_sev severity, enum mbox_btns buttons);
|
||||
////Shows a message box. You can do that by creating a label and some buttons, but it gives inferior results.
|
||||
////Returns true for OK and Yes, and false for Cancel/No/close window.
|
||||
////The title may or may not be ignored.
|
||||
//enum mbox_sev { mb_info, mb_warn, mb_err };
|
||||
//enum mbox_btns { mb_ok, mb_okcancel, mb_yesno };
|
||||
//bool window_message_box(const char * text, const char * title, enum mbox_sev severity, enum mbox_btns buttons);
|
||||
|
||||
//Usable for both ROMs and dylibs. If dylib is true, the returned filenames are for the system's
|
||||
// dynamic linker; this will disable gvfs-like systems the dynamic linker can't understand, and may
|
||||
// hide files not marked executable, if this makes sense. If false, only file_read/etc is guaranteed
|
||||
// to work.
|
||||
//If multiple is true, multiple files may be picked; if not, only one can be picked. Should
|
||||
// generally be true for dylibs and false for ROMs, but not guaranteed.
|
||||
//The parent window will be disabled while the dialog is active.
|
||||
//Both extensions and return value have the format { "smc", ".sfc", NULL }. Extensions may or may not
|
||||
// include the dot; if it's not there, it's implied.
|
||||
//Return value is full paths, zero or more. Duplicates are allowed in both input and output.
|
||||
//The return value is valid until the next call to window_file_picker() or window_run_*(), whichever comes first.
|
||||
const char * const * window_file_picker(struct window * parent,
|
||||
const char * title,
|
||||
const char * const * extensions,
|
||||
const char * extdescription,
|
||||
bool dylib,
|
||||
bool multiple);
|
||||
////Usable for both ROMs and dylibs. If dylib is true, the returned filenames are for the system's
|
||||
//// dynamic linker; this will disable gvfs-like systems the dynamic linker can't understand, and may
|
||||
//// hide files not marked executable, if this makes sense. If false, only file_read/etc is guaranteed
|
||||
//// to work.
|
||||
////If multiple is true, multiple files may be picked; if not, only one can be picked. Should
|
||||
//// generally be true for dylibs and false for ROMs, but not guaranteed.
|
||||
////The parent window will be disabled while the dialog is active.
|
||||
////Both extensions and return value have the format { "smc", ".sfc", NULL }. Extensions are optional.
|
||||
////Return value is full paths, zero or more. Duplicates are allowed in both input and output.
|
||||
////The return value is valid until the next call to window_file_picker() or window_run_*(), whichever comes first.
|
||||
//const char * const * window_file_picker(window * parent,
|
||||
// const char * title,
|
||||
// const char * const * extensions,
|
||||
// const char * extdescription,
|
||||
// bool dylib,
|
||||
// bool multiple);
|
||||
|
||||
//Returns the number of microseconds since an undefined start time.
|
||||
//The start point doesn't change while the program is running, but need not be the same across reboots, nor between two processes.
|
||||
//It can be program launch, system boot, the Unix epoch, or whatever.
|
||||
uint64_t window_get_time();
|
||||
////Returns the number of microseconds since an undefined start time.
|
||||
////The start point doesn't change while the program is running, but need not be the same across reboots, nor between two processes.
|
||||
////It can be program launch, system boot, the Unix epoch, or whatever.
|
||||
//uint64_t window_get_time();
|
||||
|
||||
//The different components may want to initialize various parts each. All three may not necessarily exist.
|
||||
//Implementation details, don't touch.
|
||||
void _window_init_inner();
|
||||
void _window_init_misc();
|
||||
void _window_init_shell();
|
||||
//If the window shell is the one told about interaction with a widget, this sends it back to the inner area.
|
||||
uintptr_t _window_notify_inner(void* notification);
|
||||
//Because Windows is a douchebag.
|
||||
uintptr_t _window_get_widget_color(unsigned int type, void* handle, void* draw, void* parent);
|
||||
|
||||
//This one can be used if the one calling widget_listbox_virtual->set_contents doesn't provide a search function.
|
||||
@@ -755,3 +770,5 @@ extern struct window_x11_info window_x11;
|
||||
#endif
|
||||
|
||||
//TODO: If porting to Qt, use https://woboq.com/blog/verdigris-qt-without-moc.html
|
||||
//Windows resources are bad enough, but at least they have a reason to exist -
|
||||
// they have to be available without executing the program. moc has no such excuse.
|
||||
|
||||
100
arlib/intarray.h
100
arlib/intarray.h
@@ -1,100 +0,0 @@
|
||||
#include "global.h"
|
||||
|
||||
//Use only with the primitive types.
|
||||
template<typename T>
|
||||
class intarray {
|
||||
public:
|
||||
//Could be optimized a lot harder, but I don't care, it's not used much.
|
||||
|
||||
T* ptr;
|
||||
size_t len;
|
||||
|
||||
static const size_t MIN_SIZE = 128/sizeof(T); // minimum number of objects this one always holds
|
||||
static const size_t MIN_SIZE_SHRINK = 4096/sizeof(T); // don't shrink smaller than this size
|
||||
static const size_t MIN_SIZE_FACTOR = 4; // only shrink by this factor or more (must be power of two)
|
||||
|
||||
private:
|
||||
size_t capacity;
|
||||
|
||||
static size_t resize_size(size_t oldlen, size_t len)
|
||||
{
|
||||
len = bitround(len);
|
||||
|
||||
if (len < MIN_SIZE) return MIN_SIZE;
|
||||
if (len > oldlen) return len;
|
||||
|
||||
if (len < oldlen/MIN_SIZE_FACTOR && oldlen>MIN_SIZE_SHRINK)
|
||||
{
|
||||
if (oldlen/MIN_SIZE_FACTOR < MIN_SIZE_SHRINK) return MIN_SIZE_SHRINK;
|
||||
else return oldlen/MIN_SIZE_FACTOR;
|
||||
}
|
||||
|
||||
return oldlen;
|
||||
}
|
||||
|
||||
void resize(size_t newcap)
|
||||
{
|
||||
newcap = resize_size(capacity, newcap);
|
||||
if (newcap == capacity) return;
|
||||
|
||||
capacity = newcap;
|
||||
ptr = realloc(ptr, sizeof(T)*capacity);
|
||||
}
|
||||
|
||||
public:
|
||||
intarray()
|
||||
{
|
||||
ptr = NULL;
|
||||
len = 0;
|
||||
capacity = 0;
|
||||
}
|
||||
|
||||
intarray(const intarray<T>& other)
|
||||
{
|
||||
ptr = NULL;
|
||||
capacity = 0;
|
||||
resize(other.len);
|
||||
len = other.len;
|
||||
memcpy(ptr, other.ptr, sizeof(T)*len);
|
||||
}
|
||||
|
||||
~intarray() { free(ptr); }
|
||||
|
||||
//Optimization - call this to reserve a chunk of space of 'len' entries. Calling append() with the same data will avoid a copy.
|
||||
//The length to append() may be smaller than here. If you don't want to append anything at all, it's fine to not call append().
|
||||
T* append_try(size_t len)
|
||||
{
|
||||
resize(this->len + len);
|
||||
return ptr+len;
|
||||
}
|
||||
|
||||
void append(const T* data, size_t len)
|
||||
{
|
||||
if (data == ptr+len)
|
||||
{
|
||||
this->len += len;
|
||||
return;
|
||||
}
|
||||
|
||||
resize(this->len + len);
|
||||
memcpy(ptr + this->len, data, sizeof(T)*len);
|
||||
this->len += len;
|
||||
}
|
||||
|
||||
void prepend(const T* data, size_t len)
|
||||
{
|
||||
resize(this->len + len);
|
||||
memmove(ptr+len, ptr, sizeof(T)*this->len);
|
||||
memcpy(ptr, data, sizeof(T)*len);
|
||||
this->len += len;
|
||||
}
|
||||
|
||||
void drop(int count)
|
||||
{
|
||||
memmove(ptr, ptr+count, sizeof(T)*(len-count));
|
||||
len -= count;
|
||||
resize(len);
|
||||
}
|
||||
};
|
||||
|
||||
using bytearray = intarray<uint8_t>;
|
||||
52
arlib/maybe.h
Normal file
52
arlib/maybe.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include "global.h"
|
||||
|
||||
//error==0 means value exists and is valid, anything else means it doesn't
|
||||
template<typename T> class maybe {
|
||||
public:
|
||||
int error;
|
||||
union {
|
||||
char dummy; // don't demand a default-initialization on the value
|
||||
T value;
|
||||
};
|
||||
|
||||
maybe(T&& data)
|
||||
{
|
||||
error = 0;
|
||||
new(&value) T(data);
|
||||
}
|
||||
maybe(null_t) : error(1) {}
|
||||
maybe(null_t, int error) : error(error) {}
|
||||
|
||||
maybe(const maybe<T>& other)
|
||||
{
|
||||
error = other.value;
|
||||
if (error==0)
|
||||
{
|
||||
new(&value) T(other.value);
|
||||
}
|
||||
}
|
||||
maybe(maybe<T>&& other)
|
||||
{
|
||||
memcpy(this, &other, sizeof(*this));
|
||||
other.error=1;
|
||||
}
|
||||
~maybe()
|
||||
{
|
||||
if (error==0) value.~T();
|
||||
}
|
||||
|
||||
explicit operator bool() { return !error; }
|
||||
bool operator!() { return !!error; }
|
||||
};
|
||||
|
||||
template<> class maybe<void> {
|
||||
public:
|
||||
int error;
|
||||
|
||||
maybe(null_t) : error(1) {}
|
||||
maybe(null_t, int error) : error(error) {}
|
||||
|
||||
explicit operator bool() { return !error; }
|
||||
bool operator!() { return !!error; }
|
||||
};
|
||||
158
arlib/opengl/aropengl.h
Normal file
158
arlib/opengl/aropengl.h
Normal file
@@ -0,0 +1,158 @@
|
||||
#pragma once
|
||||
#include "../global.h"
|
||||
#include "../gui/window.h"
|
||||
|
||||
#if !defined(_WIN32) || __has_include(<GL/glext.h>)
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#else
|
||||
#include "../deps/gl.h"
|
||||
#include "../deps/glext.h"
|
||||
#endif
|
||||
|
||||
#ifndef GLAPIENTRY
|
||||
#ifdef _WIN32
|
||||
#define GLAPIENTRY APIENTRY
|
||||
#else
|
||||
#define GLAPIENTRY
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class aropengl : nocopy {
|
||||
public:
|
||||
enum {
|
||||
t_ver_1_0 = 100, t_ver_1_1 = 110, t_ver_1_2 = 120, t_ver_1_3 = 130, t_ver_1_4 = 140, t_ver_1_5 = 150,
|
||||
t_ver_2_0 = 200, t_ver_2_1 = 210,
|
||||
t_ver_3_0 = 300, t_ver_3_1 = 310, t_ver_3_2 = 320, t_ver_3_3 = 330,
|
||||
t_ver_4_0 = 400, t_ver_4_1 = 410, t_ver_4_2 = 420, t_ver_4_3 = 430, t_ver_4_4 = 440, t_ver_4_5 = 450,
|
||||
|
||||
t_opengl_es = 0x001000, // Probably not supported.
|
||||
t_debug_context = 0x002000, // Requests a debug context. Doesn't actually enable debugging, use gl.enableDefaultDebugger or gl.DebugMessageControl/etc.
|
||||
t_depth_buffer = 0x004000, // These two only apply to the main buffer. You can always create additional FBOs with or without depth/stencil.
|
||||
t_stencil_buffer = 0x008000,
|
||||
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
//Direct3D vsync is an advanced feature that uses WGL_NV_DX_interop and D3DSWAPEFFECT_FLIPEX to ensure smooth framerate on Windows.
|
||||
//Advantages:
|
||||
//- Less stuttering, especially with DWM enabled (at least on some computers, sometimes vsync is already smooth)
|
||||
//Disadvantages:
|
||||
//- Requires Windows 7 or newer
|
||||
//- Some graphics cards and drivers are not compatible
|
||||
//- Poorly tested driver path, may be slow or buggy (in fact, I believe I found a Nvidia driver bug while creating this)
|
||||
//- You may not render to the default framebuffer, 0; you must render to gl.defaultFramebuffer()
|
||||
// (if you don't use framebuffers, you can ignore this; defaultFramebuffer is bound on creation)
|
||||
//- You must call gl.notifyResize() whenever the window is resized (whether by the application or the user), in addition to gl.Viewport/etc
|
||||
//- Swap intervals other than 0 and 1 are not supported, not even -1
|
||||
//- May be slower, especially with vsync off
|
||||
//- D/S buffers are currently not created (TODO: remove limitation)
|
||||
//The flag is ignored on non-Windows systems.
|
||||
//It is safe to use gl.defaultFramebuffer and gl.notifyResize on non-d3dsync objects.
|
||||
# ifdef _WIN32
|
||||
t_direct3d_vsync = 0x010000,
|
||||
# else
|
||||
t_direct3d_vsync = 0,
|
||||
# undef AROPENGL_D3DSYNC
|
||||
# endif
|
||||
#endif
|
||||
};
|
||||
|
||||
class context : nocopy {
|
||||
public:
|
||||
//this is basically the common subset of WGL/GLX/etc
|
||||
//you want the outer class, as it offers proper extension/symbol management
|
||||
static context* create(uintptr_t parent, uintptr_t* window, uint32_t flags);
|
||||
|
||||
virtual void makeCurrent(bool make) = 0; // If false, releases the context. The context is current on creation.
|
||||
virtual void swapInterval(int interval) = 0;
|
||||
virtual void swapBuffers() = 0;
|
||||
virtual funcptr getProcAddress(const char * proc) = 0;
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
virtual
|
||||
#endif
|
||||
void notifyResize(unsigned int width, unsigned int height) {}
|
||||
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
virtual
|
||||
#endif
|
||||
GLuint outputFramebuffer() { return 0; }
|
||||
|
||||
virtual void destroy() = 0;
|
||||
//implementations must ensure the destructor is safe even after having its window destroyed
|
||||
//the best method is putting everything into destroy() and having the destructor just call that
|
||||
virtual ~context() {}
|
||||
};
|
||||
|
||||
bool create(context* core);
|
||||
|
||||
bool create(uintptr_t parent, uintptr_t* window, uint32_t flags)
|
||||
{
|
||||
return create(context::create(parent, window, flags));
|
||||
}
|
||||
|
||||
bool create(widget_viewport* port, uint32_t flags)
|
||||
{
|
||||
uintptr_t newwindow;
|
||||
if (!create(port->get_parent(), &newwindow, flags)) return false;
|
||||
this->port = port;
|
||||
port->set_child(newwindow,
|
||||
bind_ptr(&aropengl::context::notifyResize, this->core),
|
||||
bind_ptr(&aropengl::destroy, this));
|
||||
return true;
|
||||
}
|
||||
|
||||
aropengl() { create(NULL); }
|
||||
aropengl(context* core) { create(core); }
|
||||
aropengl(uintptr_t parent, uintptr_t* window, uint32_t flags) { create(parent, window, flags); }
|
||||
aropengl(widget_viewport* port, uint32_t flags) { create(port, flags); }
|
||||
explicit operator bool() { return core!=NULL; }
|
||||
|
||||
~aropengl()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
//Arlib usually uses underscores, but since OpenGL doesn't, this object follows suit.
|
||||
//To ensure no collisions, Arlib-specific functions start with a lowercase (or are C++-only, like operator bool), standard GL functions are uppercase.
|
||||
|
||||
//If false, releases the context. The context is current on creation.
|
||||
void makeCurrent(bool make) { core->makeCurrent(make); }
|
||||
void swapInterval(int interval) { core->swapInterval(interval); }
|
||||
void swapBuffers() { core->swapBuffers(); }
|
||||
funcptr getProcAddress(const char * proc) { return core->getProcAddress(proc); }
|
||||
|
||||
//If the window is resized, use this function to report the new size.
|
||||
//Not needed if the object is created from a viewport.
|
||||
void notifyResize(GLsizei width, GLsizei height) { core->notifyResize(width, height); }
|
||||
//Used only for Direct3D sync. If you're not using that, just use 0.
|
||||
GLuint outputFramebuffer() { return core->outputFramebuffer(); }
|
||||
|
||||
//Releases all resources owned by the object; the object may not be used after this.
|
||||
//Use if the destructor isn't guaranteed to run while the driver's window still exists.
|
||||
//Not needed if the object is created from a viewport.
|
||||
void destroy()
|
||||
{
|
||||
if (port)
|
||||
{
|
||||
port->set_child(0, NULL, NULL);
|
||||
port = NULL;
|
||||
}
|
||||
delete core;
|
||||
core = NULL;
|
||||
}
|
||||
|
||||
//void (GLAPIENTRY * ClearColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
|
||||
//void (GLAPIENTRY * Clear)(GLbitfield mask);
|
||||
//etc
|
||||
#define AROPENGL_GEN_HEADER
|
||||
#include "generated.c"
|
||||
#undef AROPENGL_GEN_HEADER
|
||||
//It's intended that this object is named 'gl', resulting in gl.Clear(GL_etc), somewhat like WebGLRenderingContext.
|
||||
//It is not guaranteed that a non-NULL function will actually work, or even successfully return. Check gl.hasExtension.
|
||||
|
||||
bool hasExtension(const char * ext);
|
||||
void enableDefaultDebugger(FILE* out = NULL); //Use only if the context was created with the debug flag.
|
||||
|
||||
private:
|
||||
context* core;
|
||||
widget_viewport* port;
|
||||
};
|
||||
501
arlib/opengl/ctx-windows.cpp
Normal file
501
arlib/opengl/ctx-windows.cpp
Normal file
@@ -0,0 +1,501 @@
|
||||
#ifdef _WIN32
|
||||
#include "aropengl.h"
|
||||
|
||||
//https://www.opengl.org/registry/specs/NV/DX_interop.txt
|
||||
//https://github.com/halogenica/WGL_NV_DX/blob/master/SharedResource.cpp
|
||||
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb174336(v=vs.85).aspx
|
||||
|
||||
|
||||
#undef bind
|
||||
#ifdef _MSC_VER
|
||||
//MSVC's gl.h doesn't seem to include the stuff it should. Copying these five lines from mingw's gl.h...
|
||||
# if !(defined(WINGDIAPI) && defined(APIENTRY))
|
||||
# include <windows.h>
|
||||
# else
|
||||
# include <stddef.h>
|
||||
# endif
|
||||
//Also disable a block of code that defines int32_t to something not identical to my msvc-compatible stdint.h.
|
||||
# define GLEXT_64_TYPES_DEFINED
|
||||
#endif
|
||||
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<GL/wglext.h>)
|
||||
#include <GL/wglext.h>
|
||||
#endif
|
||||
#endif
|
||||
#ifndef WGL_WGLEXT_VERSION
|
||||
#include "../deps/wglext.h"
|
||||
#endif
|
||||
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
# include <D3D9.h>
|
||||
|
||||
# ifndef D3DPRESENT_FORCEIMMEDIATE
|
||||
# define D3DPRESENT_FORCEIMMEDIATE 0x00000100L
|
||||
# endif
|
||||
# ifndef D3DPRESENT_DONOTWAIT
|
||||
# define D3DPRESENT_DONOTWAIT 0x00000001L
|
||||
# endif
|
||||
|
||||
# define IFD3D(x) x
|
||||
#else
|
||||
# define IFD3D(x)
|
||||
#endif
|
||||
|
||||
#define bind bind_func
|
||||
|
||||
namespace {
|
||||
|
||||
#define WGL_SYMS() \
|
||||
WGL_SYM(HGLRC, CreateContext, (HDC hdc)) \
|
||||
WGL_SYM(BOOL, DeleteContext, (HGLRC hglrc)) \
|
||||
WGL_SYM(HGLRC, GetCurrentContext, ()) \
|
||||
WGL_SYM(PROC, GetProcAddress, (LPCSTR lpszProc)) \
|
||||
WGL_SYM(BOOL, MakeCurrent, (HDC hdc, HGLRC hglrc)) \
|
||||
|
||||
//WINGDIAPI BOOL WINAPI wglCopyContext(HGLRC, HGLRC, UINT);
|
||||
//WINGDIAPI HGLRC WINAPI wglCreateContext(HDC);
|
||||
//WINGDIAPI HGLRC WINAPI wglCreateLayerContext(HDC, int);
|
||||
//WINGDIAPI BOOL WINAPI wglDeleteContext(HGLRC);
|
||||
//WINGDIAPI HGLRC WINAPI wglGetCurrentContext(VOID);
|
||||
//WINGDIAPI HDC WINAPI wglGetCurrentDC(VOID);
|
||||
//WINGDIAPI PROC WINAPI wglGetProcAddress(LPCSTR);
|
||||
//WINGDIAPI BOOL WINAPI wglMakeCurrent(HDC, HGLRC);
|
||||
//WINGDIAPI BOOL WINAPI wglShareLists(HGLRC, HGLRC);
|
||||
//WINGDIAPI BOOL WINAPI wglUseFontBitmapsA(HDC, DWORD, DWORD, DWORD);
|
||||
//WINGDIAPI BOOL WINAPI wglUseFontBitmapsW(HDC, DWORD, DWORD, DWORD);
|
||||
//#ifdef UNICODE
|
||||
//#define wglUseFontBitmaps wglUseFontBitmapsW
|
||||
//#else
|
||||
//#define wglUseFontBitmaps wglUseFontBitmapsA
|
||||
//#endif // !UNICODE
|
||||
//WINGDIAPI BOOL WINAPI SwapBuffers(HDC);
|
||||
|
||||
//SwapBuffers and various others are actually in gdi32.dll, which is used elsewhere and can safely be included here too
|
||||
|
||||
#define WGL_EXTS() \
|
||||
WGL_EXT(PFNWGLSWAPINTERVALEXTPROC, SwapIntervalEXT) /* must be first */ \
|
||||
IFD3D( \
|
||||
WGL_EXT(PFNWGLDXSETRESOURCESHAREHANDLENVPROC, DXSetResourceShareHandleNV) \
|
||||
WGL_EXT(PFNWGLDXOPENDEVICENVPROC, DXOpenDeviceNV) \
|
||||
WGL_EXT(PFNWGLDXCLOSEDEVICENVPROC, DXCloseDeviceNV) \
|
||||
WGL_EXT(PFNWGLDXREGISTEROBJECTNVPROC, DXRegisterObjectNV) \
|
||||
WGL_EXT(PFNWGLDXUNREGISTEROBJECTNVPROC, DXUnregisterObjectNV) \
|
||||
WGL_EXT(PFNWGLDXOBJECTACCESSNVPROC, DXObjectAccessNV) \
|
||||
WGL_EXT(PFNWGLDXLOCKOBJECTSNVPROC, DXLockObjectsNV) \
|
||||
WGL_EXT(PFNWGLDXUNLOCKOBJECTSNVPROC, DXUnlockObjectsNV) \
|
||||
) \
|
||||
|
||||
struct {
|
||||
#define WGL_SYM(ret, name, args) ret (WINAPI * name) args;
|
||||
WGL_SYMS()
|
||||
#undef WGL_SYM
|
||||
#define WGL_EXT(type, name) type name;
|
||||
WGL_EXTS()
|
||||
#undef WGL_EXT
|
||||
HMODULE lib;
|
||||
} static wgl;
|
||||
#define WGL_SYM(ret, name, args) "wgl" #name "\0"
|
||||
static const char wgl_proc_names[] = WGL_SYMS() ;
|
||||
#undef WGL_SYM
|
||||
#define WGL_EXT(type, name) "wgl" #name "\0"
|
||||
static const char wgl_ext_names[] = WGL_EXTS() ;
|
||||
#undef WGL_EXT
|
||||
|
||||
|
||||
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
typedef HRESULT (WINAPI * Direct3DCreate9Ex_t)(UINT SDKVersion, IDirect3D9Ex* * ppD3D);
|
||||
static HMODULE hD3D9=NULL;
|
||||
static Direct3DCreate9Ex_t lpDirect3DCreate9Ex;
|
||||
|
||||
static bool libLoadD3D()
|
||||
{
|
||||
hD3D9=LoadLibrary("d3d9.dll");
|
||||
if (!hD3D9) return false;
|
||||
//lpDirect3DCreate9=Direct3DCreate9;//these are for verifying that Direct3DCreate9Ex_t matches the real function; they're not needed anymore
|
||||
//lpDirect3DCreate9Ex=Direct3DCreate9Ex;
|
||||
lpDirect3DCreate9Ex=(Direct3DCreate9Ex_t)GetProcAddress(hD3D9, "Direct3DCreate9Ex");
|
||||
if (!lpDirect3DCreate9Ex) { FreeLibrary(hD3D9); return false; }
|
||||
//if (!lpDirect3DCreate9Ex) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void libUnloadD3D()
|
||||
{
|
||||
FreeLibrary(hD3D9);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool libLoadGL()
|
||||
{
|
||||
//this can yield multiple unsynchronized writers to global variables
|
||||
//however, this is safe, because they all write the same values in the same order
|
||||
//(except if writing that cache line also discards other changes to the same cache line, but that just won't happen.)
|
||||
wgl.lib = LoadLibrary("opengl32.dll");
|
||||
if (!wgl.lib) return false;
|
||||
|
||||
//HMODULE gdilib=GetModuleHandle("gdi32.dll");
|
||||
|
||||
const char * names = wgl_proc_names;
|
||||
FARPROC* functions = (FARPROC*)&wgl;
|
||||
|
||||
while (*names)
|
||||
{
|
||||
*functions = GetProcAddress(wgl.lib, names);
|
||||
if (!*functions) return false;
|
||||
|
||||
functions++;
|
||||
names += strlen(names)+1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void libUnloadGL()
|
||||
{
|
||||
if (wgl.lib) FreeLibrary(wgl.lib);
|
||||
}
|
||||
|
||||
class aropengl_windows : public aropengl::context {
|
||||
public:
|
||||
HWND GL_hwnd;
|
||||
HDC GL_hdc;
|
||||
HGLRC GL_hglrc;
|
||||
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
bool d3dsync;
|
||||
|
||||
HWND D3D_hwnd;
|
||||
|
||||
IDirect3DDevice9Ex* D3D_device;
|
||||
IDirect3DSurface9* D3D_backbuf;
|
||||
IDirect3DSurface9* D3D_GLtarget;
|
||||
|
||||
HANDLE D3D_sharehandle;
|
||||
HANDLE D3D_sharetexture;
|
||||
HANDLE GL_htexture;
|
||||
|
||||
GLuint GL_fboname;
|
||||
GLuint GL_texturename;
|
||||
|
||||
bool vsync;
|
||||
#endif
|
||||
|
||||
/*private*/ bool init(HWND parent, HWND* window_, uint32_t flags)
|
||||
{
|
||||
DWORD glwndflags = WS_CHILD | WS_VISIBLE;
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
this->d3dsync = (flags & aropengl::t_direct3d_vsync);
|
||||
if (this->d3dsync) glwndflags &= ~WS_VISIBLE;
|
||||
#endif
|
||||
|
||||
this->GL_hwnd = CreateWindow("arlib", NULL, glwndflags, 0, 0, 1, 1, parent, NULL, NULL, NULL);
|
||||
|
||||
*window_ = this->GL_hwnd;
|
||||
this->GL_hdc = GetDC(this->GL_hwnd);
|
||||
this->GL_hglrc = NULL;
|
||||
|
||||
if (!libLoadGL()) return false;
|
||||
if (!CreateContext(flags)) return false;
|
||||
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
if (this->d3dsync)
|
||||
{
|
||||
this->D3D_hwnd = CreateWindow("arlib", NULL, WS_CHILD | WS_VISIBLE, 0, 0, 1, 1, parent, NULL, NULL, NULL);
|
||||
*window_ = this->D3D_hwnd;
|
||||
D3D_sharehandle = NULL;
|
||||
D3D_sharetexture = NULL;
|
||||
GL_htexture = NULL;
|
||||
|
||||
if (!libLoadD3D()) return false;
|
||||
if (!CreateD3DContext()) return false;
|
||||
if (!JoinGLD3D()) return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///*private*/ HWND CreateDummyWindow(HWND parent)
|
||||
//{
|
||||
// WNDCLASS wc = {};
|
||||
// wc.lpfnWndProc = DefWindowProc;
|
||||
// wc.lpszClassName = "arlib_opengl_dummy";
|
||||
// RegisterClass(&wc);
|
||||
//
|
||||
// return CreateWindow("arlib", NULL, WS_CHILD, -1, 0, 1, 1, parent, NULL, NULL, NULL);
|
||||
// //return CreateWindow("arlib", "OPENGL", WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL);
|
||||
//}
|
||||
|
||||
/*private*/ bool CreateContext(uint32_t flags)
|
||||
{
|
||||
if (wgl.GetCurrentContext()) return false;
|
||||
|
||||
bool debug = (flags & aropengl::t_debug_context);
|
||||
bool depthbuf = (flags & aropengl::t_depth_buffer);
|
||||
bool stenbuf = (flags & aropengl::t_stencil_buffer);
|
||||
uint32_t version = (flags & 0xFFF);
|
||||
|
||||
PIXELFORMATDESCRIPTOR pfd;
|
||||
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
|
||||
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
|
||||
pfd.nVersion = 1;
|
||||
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
||||
pfd.iPixelType = PFD_TYPE_RGBA;
|
||||
pfd.cColorBits = 24;
|
||||
pfd.cAlphaBits = 0;
|
||||
pfd.cAccumBits = 0;
|
||||
pfd.cDepthBits = (stenbuf ? 24 : depthbuf ? 16 : 0);
|
||||
pfd.cStencilBits = (stenbuf ? 8 : 0);
|
||||
pfd.cAuxBuffers = 0;
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
if (this->d3dsync)
|
||||
{
|
||||
pfd.dwFlags &= ~PFD_DOUBLEBUFFER;
|
||||
pfd.cDepthBits = 0;
|
||||
pfd.cStencilBits = 0;
|
||||
}
|
||||
#endif
|
||||
pfd.iLayerType = PFD_MAIN_PLANE;
|
||||
SetPixelFormat(this->GL_hdc, ChoosePixelFormat(this->GL_hdc, &pfd), &pfd);
|
||||
this->GL_hglrc = wgl.CreateContext(this->GL_hdc);
|
||||
if (!this->GL_hglrc) return false;
|
||||
|
||||
wgl.MakeCurrent(this->GL_hdc, this->GL_hglrc);
|
||||
|
||||
if (version >= 310)
|
||||
{
|
||||
HGLRC hglrc_old = this->GL_hglrc;
|
||||
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs =
|
||||
(PFNWGLCREATECONTEXTATTRIBSARBPROC)wgl.GetProcAddress("wglCreateContextAttribsARB");
|
||||
const int attribs[] = {
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB, (int)version/100,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB, (int)version/10%10,
|
||||
WGL_CONTEXT_FLAGS_ARB, debug ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
|
||||
//WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
|
||||
//https://www.opengl.org/wiki/Core_And_Compatibility_in_Contexts says do not use
|
||||
0 };
|
||||
this->GL_hglrc = wglCreateContextAttribs(this->GL_hdc, /*share*/NULL, attribs);
|
||||
wgl.DeleteContext(hglrc_old);
|
||||
|
||||
if (!this->GL_hglrc)
|
||||
{
|
||||
wgl.MakeCurrent(NULL, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
wgl.MakeCurrent(this->GL_hdc, this->GL_hglrc);
|
||||
}
|
||||
|
||||
const char * names = wgl_ext_names;
|
||||
FARPROC* functions = (FARPROC*)&wgl.SwapIntervalEXT;
|
||||
|
||||
while (*names)
|
||||
{
|
||||
*functions = wgl.GetProcAddress(names);
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
if (d3dsync && !*functions) return false; // this demands wglSwapIntervalEXT even for d3d sync, but that one is supported by everything.
|
||||
#endif
|
||||
|
||||
functions++;
|
||||
names += strlen(names)+1;
|
||||
}
|
||||
if (!wgl.SwapIntervalEXT) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
/*private*/ bool CreateD3DContext()
|
||||
{
|
||||
IDirect3D9Ex* d3d;
|
||||
this->D3D_device = NULL;
|
||||
|
||||
if (FAILED(lpDirect3DCreate9Ex(D3D_SDK_VERSION, &d3d))) return false;
|
||||
|
||||
D3DPRESENT_PARAMETERS parameters = {};
|
||||
parameters.BackBufferCount = 2; // D3DPRESENT_FORCEIMMEDIATE|D3DPRESENT_DONOTWAIT doesn't work without this
|
||||
parameters.SwapEffect = D3DSWAPEFFECT_FLIPEX;
|
||||
parameters.hDeviceWindow = this->D3D_hwnd;
|
||||
parameters.Windowed = TRUE;
|
||||
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb172585(v=vs.85).aspx
|
||||
//_ONE is _DEFAULT, but also calls timeBeginPeriod to improve precision
|
||||
//anything opting in to Direct3D vsync is clearly a high-performance program, and thus wants the increased precision
|
||||
parameters.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
|
||||
|
||||
if (FAILED(d3d->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, this->D3D_hwnd,
|
||||
D3DCREATE_MIXED_VERTEXPROCESSING|D3DCREATE_MULTITHREADED,
|
||||
¶meters, NULL, &this->D3D_device)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
d3d->Release();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*private*/ bool JoinGLD3D()
|
||||
{
|
||||
//a bit untidy, but this object doesn't have access to the real one
|
||||
#define SYM(type, name) type name = (type)this->getProcAddress(STR(name)); if (!name) return false
|
||||
typedef void (GLAPIENTRY * PFNGLGENTEXTURES)(GLsizei n, GLuint *textures);
|
||||
typedef void (GLAPIENTRY * PFNGLBINDTEXTURE)(GLenum target, GLuint texture);
|
||||
SYM(PFNGLGENTEXTURES, glGenTextures);
|
||||
SYM(PFNGLBINDTEXTURE, glBindTexture);
|
||||
SYM(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers);
|
||||
SYM(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer);
|
||||
SYM(PFNGLFRAMEBUFFERTEXTUREPROC, glFramebufferTexture);
|
||||
#undef SYM
|
||||
|
||||
glGenTextures(1, &GL_texturename);
|
||||
glBindTexture(GL_TEXTURE_2D, GL_texturename);
|
||||
|
||||
D3D_sharehandle = wgl.DXOpenDeviceNV(this->D3D_device);
|
||||
|
||||
this->D3D_device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &this->D3D_backbuf);
|
||||
|
||||
AllocRenderTarget();
|
||||
|
||||
//the framebuffer must be bound after calling AllocRenderTarget, or the Nvidia driver claims the framebuffer is incomplete
|
||||
//this bug can be fixed by querying the current FBO and binding that, which shouldn't have any effect
|
||||
//additionally, the Intel driver is happy with either order
|
||||
//I suspect driver bug of some kind
|
||||
glGenFramebuffers(1, &GL_fboname);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, GL_fboname);
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_texturename, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*private*/ void AllocRenderTarget()
|
||||
{
|
||||
RECT wndsize;
|
||||
GetClientRect(this->D3D_hwnd, &wndsize);
|
||||
this->D3D_device->CreateRenderTarget(wndsize.right, wndsize.bottom, D3DFMT_X8R8G8B8, D3DMULTISAMPLE_NONE, 0, false, &this->D3D_GLtarget, &D3D_sharetexture);
|
||||
|
||||
wgl.DXSetResourceShareHandleNV(this->D3D_GLtarget, D3D_sharetexture);
|
||||
this->GL_htexture = wgl.DXRegisterObjectNV(this->D3D_sharehandle, this->D3D_GLtarget, GL_texturename, GL_TEXTURE_2D, WGL_ACCESS_WRITE_DISCARD_NV);
|
||||
|
||||
wgl.DXLockObjectsNV(D3D_sharehandle, 1, &this->GL_htexture);
|
||||
}
|
||||
|
||||
/*private*/ void DeallocRenderTarget()
|
||||
{
|
||||
if (!this->D3D_GLtarget) return;
|
||||
|
||||
wgl.DXUnlockObjectsNV(D3D_sharehandle, 1, &this->GL_htexture);
|
||||
wgl.DXUnregisterObjectNV(D3D_sharehandle, this->GL_htexture);
|
||||
|
||||
this->D3D_GLtarget->Release();
|
||||
this->D3D_GLtarget = NULL;
|
||||
|
||||
//those D3D share handles are weird stuff like 0xC0007000, closing them throws errors
|
||||
//I'll assume it's freed by deleting the rendertarget, device or IDirect3D9Ex, and that reusing it does not leak
|
||||
//CloseHandle(D3D_sharetexture);
|
||||
//D3D_sharetexture = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
void makeCurrent(bool make)
|
||||
{
|
||||
if (make) wgl.MakeCurrent(this->GL_hdc, this->GL_hglrc);
|
||||
else wgl.MakeCurrent(NULL, NULL);
|
||||
}
|
||||
|
||||
funcptr getProcAddress(const char * proc)
|
||||
{
|
||||
PROC ret = wgl.GetProcAddress(proc);
|
||||
if (!ret) ret = ::GetProcAddress(wgl.lib, proc); // lol windows
|
||||
return (funcptr)ret;
|
||||
}
|
||||
|
||||
void swapInterval(int interval)
|
||||
{
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
if (d3dsync) vsync = (interval==1);
|
||||
else
|
||||
#endif
|
||||
wgl.SwapIntervalEXT(interval);
|
||||
}
|
||||
|
||||
void swapBuffers()
|
||||
{
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
if (d3dsync)
|
||||
{
|
||||
wgl.DXUnlockObjectsNV(D3D_sharehandle, 1, &this->GL_htexture);
|
||||
this->D3D_device->StretchRect(this->D3D_GLtarget, NULL, this->D3D_backbuf, NULL, D3DTEXF_NONE);
|
||||
this->D3D_device->PresentEx(NULL, NULL, NULL, NULL, (vsync ? 0 : D3DPRESENT_FORCEIMMEDIATE|D3DPRESENT_DONOTWAIT));
|
||||
wgl.DXLockObjectsNV(D3D_sharehandle, 1, &this->GL_htexture);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
::SwapBuffers(this->GL_hdc);
|
||||
}
|
||||
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
void notifyResize(GLsizei width, GLsizei height)
|
||||
{
|
||||
if (d3dsync)
|
||||
{
|
||||
DeallocRenderTarget();
|
||||
AllocRenderTarget();
|
||||
}
|
||||
}
|
||||
|
||||
GLuint outputFramebuffer()
|
||||
{
|
||||
if (d3dsync) return GL_fboname;
|
||||
else return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void destroy()
|
||||
{
|
||||
//early return to ensure the libUnload functions aren't called too much
|
||||
//this is the first member set in init()
|
||||
if (!this->GL_hwnd) return;
|
||||
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
if (this->d3dsync)
|
||||
{
|
||||
DeallocRenderTarget();
|
||||
if (D3D_sharehandle) wgl.DXCloseDeviceNV(D3D_sharehandle);
|
||||
|
||||
if (this->D3D_device) this->D3D_device->Release();
|
||||
if (this->D3D_backbuf) this->D3D_backbuf->Release();
|
||||
|
||||
//don't bother cleaning up the GL resources, wglDeleteContext does that already
|
||||
if (this->D3D_hwnd) DestroyWindow(this->D3D_hwnd);
|
||||
|
||||
libUnloadD3D();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (this->GL_hglrc && wgl.DeleteContext) wgl.DeleteContext(this->GL_hglrc);
|
||||
if (this->GL_hdc) ReleaseDC(this->GL_hwnd, this->GL_hdc);
|
||||
|
||||
DestroyWindow(this->GL_hwnd);
|
||||
this->GL_hwnd = NULL;
|
||||
|
||||
libUnloadGL();
|
||||
}
|
||||
|
||||
~aropengl_windows() { destroy(); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
aropengl::context* aropengl::context::create(uintptr_t parent, uintptr_t* window, uint32_t flags)
|
||||
{
|
||||
aropengl_windows* ret = new aropengl_windows();
|
||||
if (ret->init((HWND)parent, (HWND*)window, flags)) return ret;
|
||||
|
||||
delete ret;
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
197
arlib/opengl/ctx-x11.cpp
Normal file
197
arlib/opengl/ctx-x11.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
#ifdef ARGUIPROT_X11
|
||||
#include "aropengl.h"
|
||||
|
||||
//TODO: wipe -lGL dependency
|
||||
//TODO: fix SwapInterval
|
||||
//TODO: wipe printfs
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#include <GL/glx.h>
|
||||
#include <GL/glxext.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
namespace {
|
||||
|
||||
#define GLX_SYMS() \
|
||||
/* GLX 1.0 */ \
|
||||
GLX_SYM(funcptr, GetProcAddress, (const GLubyte * procName)) \
|
||||
GLX_SYM(void, SwapBuffers, (Display* dpy, GLXDrawable drawable)) \
|
||||
GLX_SYM(Bool, MakeCurrent, (Display* dpy, GLXDrawable drawable, GLXContext ctx)) \
|
||||
GLX_SYM(Bool, QueryVersion, (Display* dpy, int* major, int* minor)) \
|
||||
GLX_SYM(GLXContext, GetCurrentContext, ()) \
|
||||
/* GLX 1.3 */ \
|
||||
GLX_SYM(GLXFBConfig*, ChooseFBConfig, (Display* dpy, int screen, const int * attrib_list, int * nelements)) \
|
||||
GLX_SYM(XVisualInfo*, GetVisualFromFBConfig, (Display* dpy, GLXFBConfig config)) \
|
||||
GLX_SYM(int, GetFBConfigAttrib, (Display* dpy, GLXFBConfig config, int attribute, int* value)) \
|
||||
GLX_SYM(void, DestroyContext, (Display* dpy, GLXContext ctx)) \
|
||||
|
||||
struct {
|
||||
#define GLX_SYM(ret, name, args) ret (*name) args;
|
||||
GLX_SYMS()
|
||||
#undef GLX_SYM
|
||||
PFNGLXSWAPINTERVALSGIPROC SwapIntervalSGI;
|
||||
void* lib;
|
||||
} static glx;
|
||||
#define GLX_SYM(ret, name, args) "glX" #name "\0"
|
||||
static const char glx_proc_names[]={ GLX_SYMS() };
|
||||
#undef GLX_SYM
|
||||
|
||||
bool libLoad()
|
||||
{
|
||||
glx.lib = dlopen("libGL.so", RTLD_LAZY);
|
||||
if (!glx.lib) return false;
|
||||
|
||||
const char * names = glx_proc_names;
|
||||
funcptr* functions=(funcptr*)&glx;
|
||||
while (*names)
|
||||
{
|
||||
*functions = (funcptr)dlsym(glx.lib, names);
|
||||
if (!*functions) return false;
|
||||
|
||||
functions++;
|
||||
names += strlen(names)+1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void libUnload()
|
||||
{
|
||||
if (glx.lib) dlclose(glx.lib);
|
||||
}
|
||||
|
||||
|
||||
|
||||
class aropengl_x11 : public aropengl::context {
|
||||
public:
|
||||
GLXContext ctx;
|
||||
Window win;
|
||||
bool current;
|
||||
|
||||
/*private*/ bool init(Window parent, Window* window_, uint32_t flags)
|
||||
{
|
||||
ctx = None;
|
||||
win = None;
|
||||
current = False;
|
||||
|
||||
if (!libLoad()) return false;
|
||||
if (glx.GetCurrentContext()) return false;
|
||||
|
||||
bool debug = (flags & aropengl::t_debug_context);
|
||||
bool depthbuf = (flags & aropengl::t_depth_buffer);
|
||||
bool stenbuf = (flags & aropengl::t_stencil_buffer);
|
||||
uint32_t version = (flags & 0xFFF);
|
||||
|
||||
int glx_major = 0;
|
||||
int glx_minor = 0;
|
||||
if (!glx.QueryVersion( window_x11.display, &glx_major, &glx_minor )) return false;
|
||||
if (glx_major != 1 || glx_minor < 3) return false;
|
||||
|
||||
// Get a matching FB config
|
||||
int visual_attribs[] = {
|
||||
GLX_X_RENDERABLE, True,
|
||||
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
|
||||
GLX_RED_SIZE, 8,
|
||||
GLX_GREEN_SIZE, 8,
|
||||
GLX_BLUE_SIZE, 8,
|
||||
GLX_DEPTH_SIZE, (stenbuf ? 24 : depthbuf ? 16 : 0),
|
||||
GLX_STENCIL_SIZE, (stenbuf ? 8 : 0),
|
||||
GLX_DOUBLEBUFFER, True,
|
||||
None
|
||||
};
|
||||
|
||||
int fbcount;
|
||||
GLXFBConfig* fbcs = glx.ChooseFBConfig(window_x11.display, window_x11.screen, visual_attribs, &fbcount);
|
||||
if (!fbcs) return false;
|
||||
GLXFBConfig fbc = fbcs[0];
|
||||
XFree(fbcs);
|
||||
|
||||
XVisualInfo* vi = glx.GetVisualFromFBConfig( window_x11.display, fbc );
|
||||
|
||||
XSetWindowAttributes swa;
|
||||
swa.colormap = XCreateColormap(window_x11.display, parent, vi->visual, AllocNone );
|
||||
swa.background_pixmap = None;
|
||||
swa.border_pixel = 0;
|
||||
swa.event_mask = StructureNotifyMask;
|
||||
|
||||
win = XCreateWindow(window_x11.display, parent, 0, 0, 1, 1, 0,
|
||||
vi->depth, InputOutput, vi->visual, CWBorderPixel|CWColormap|CWEventMask, &swa );
|
||||
if (!win) return false;
|
||||
|
||||
*window_ = win;
|
||||
XFreeColormap(window_x11.display, swa.colormap);
|
||||
XFree(vi);
|
||||
|
||||
XMapWindow(window_x11.display, win);
|
||||
|
||||
PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB =
|
||||
(PFNGLXCREATECONTEXTATTRIBSARBPROC)glx.GetProcAddress((const GLubyte*)"glXCreateContextAttribsARB");
|
||||
if (!glXCreateContextAttribsARB) return false;
|
||||
|
||||
int context_attribs[] = {
|
||||
GLX_CONTEXT_MAJOR_VERSION_ARB, (int)version/100,
|
||||
GLX_CONTEXT_MINOR_VERSION_ARB, (int)version/10%10,
|
||||
GLX_CONTEXT_FLAGS_ARB, debug ? GLX_CONTEXT_DEBUG_BIT_ARB : 0,
|
||||
None
|
||||
};
|
||||
|
||||
ctx = glXCreateContextAttribsARB(window_x11.display, fbc, 0, True, context_attribs);
|
||||
if (!ctx) return false;
|
||||
|
||||
XSync(window_x11.display, False);
|
||||
|
||||
makeCurrent(true);
|
||||
glx.SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)glx.GetProcAddress((const GLubyte*)"glXSwapIntervalSGI");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void makeCurrent(bool make)
|
||||
{
|
||||
if (make) glx.MakeCurrent( window_x11.display, win, ctx );
|
||||
else glx.MakeCurrent( window_x11.display, None, NULL );
|
||||
}
|
||||
|
||||
funcptr getProcAddress(const char * proc)
|
||||
{
|
||||
return (funcptr)glx.GetProcAddress((GLubyte*)proc);
|
||||
}
|
||||
|
||||
void swapInterval(int interval)
|
||||
{
|
||||
//EXT isn't supported on my glx client/server
|
||||
//MESA isn't in my headers
|
||||
//that leaves only one
|
||||
glx.SwapIntervalSGI(interval);
|
||||
}
|
||||
|
||||
void swapBuffers()
|
||||
{
|
||||
glx.SwapBuffers(window_x11.display, win);
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
glx.MakeCurrent(window_x11.display, None, NULL);
|
||||
glx.DestroyContext(window_x11.display, ctx);
|
||||
|
||||
XDestroyWindow(window_x11.display, win);
|
||||
libUnload();
|
||||
}
|
||||
|
||||
~aropengl_x11() { destroy(); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
aropengl::context* aropengl::context::create(uintptr_t parent, uintptr_t* window, uint32_t flags)
|
||||
{
|
||||
aropengl_x11* ret = new aropengl_x11();
|
||||
if (ret->init((Window)parent, (Window*)window, flags)) return ret;
|
||||
|
||||
delete ret;
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
106
arlib/opengl/generate.py
Normal file
106
arlib/opengl/generate.py
Normal file
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
filter_std = "-f" in sys.argv # only portable (unsuffixed, EXT, ARB or OES) functions; this is about 1900, though many are dupes of core functions
|
||||
filter_core = "-ff" in sys.argv # only core (unsuffixed) functions; this is about 1000
|
||||
filter_used = "-fff" in sys.argv # only functions used by the program, should be about 100; only works if your interface object is named 'gl';
|
||||
#unfiltered is about 2600 functions
|
||||
|
||||
def readfile(name):
|
||||
return open(name, 'rt').read()
|
||||
|
||||
def writefile(name, data):
|
||||
open(name, 'wt').write(data)
|
||||
|
||||
def collapse(str):
|
||||
return ' '.join(str.split())
|
||||
|
||||
def compile(header):
|
||||
lines = header.split("\n")
|
||||
typedefs = {}
|
||||
funcs = []
|
||||
for i,line in enumerate(lines):
|
||||
if line.startswith("GLAPI") or line.startswith("WINGDIAPI"):
|
||||
func = line
|
||||
while ")" not in func:
|
||||
i += 1
|
||||
func += lines[i]
|
||||
funcs.append(func)
|
||||
if line.startswith("typedef") and "(" in line:
|
||||
name = line.split(")")[0]
|
||||
name = name.split("(")[1]
|
||||
name = name.split(" ")[1]
|
||||
typedefs[name] = True
|
||||
out = []
|
||||
for func in funcs:
|
||||
func = func.replace("GLAPIENTRY","APIENTRY").replace("WINGDIAPI","GLAPI")
|
||||
ret = collapse(func.split("APIENTRY")[0].split("GLAPI")[1])
|
||||
name = collapse(func.split("APIENTRY")[1].split("(")[0])[2:]
|
||||
args = collapse(func.split("(")[1].split(")")[0])
|
||||
out.append({ "name": name, "ret": ret, "args": args })
|
||||
out.sort(key = lambda func: func["name"])
|
||||
return out
|
||||
|
||||
def extract_gl(code):
|
||||
import re
|
||||
code = re.sub("//.*", "", code)
|
||||
return set(re.findall("gl.([A-Za-z0-9]*)", code))
|
||||
|
||||
def filter(functions, used):
|
||||
return [f for f in functions if f["name"] in used]
|
||||
|
||||
|
||||
header = readfile("../deps/gl.h")
|
||||
header += readfile("../deps/glext.h")
|
||||
|
||||
functions = compile(header)
|
||||
|
||||
|
||||
def isupperonly(str):
|
||||
return str.isalpha() and str.isupper()
|
||||
|
||||
if filter_core:
|
||||
functions = [f for f in functions if not isupperonly(f["name"][-2:])]
|
||||
|
||||
if filter_std:
|
||||
functions = [f for f in functions if not isupperonly(f["name"][-2:]) or
|
||||
f["name"].endswith("EXT") or f["name"].endswith("ARB") or f["name"].endswith("OES")]
|
||||
|
||||
if filter_used:
|
||||
import fnmatch
|
||||
import os
|
||||
|
||||
used = []
|
||||
for root, dirs, files in os.walk("../.."):
|
||||
for filename in files:
|
||||
if filename.endswith((".c", ".cpp")) and filename!="generated.c":
|
||||
used += extract_gl(readfile(os.path.join(root, filename)))
|
||||
|
||||
functions = filter(functions, used)
|
||||
|
||||
|
||||
with open("generated.c", "wt") as out:
|
||||
out.write("""// Autogenerated, do not edit. All changes will be undone.
|
||||
|
||||
#if defined(AROPENGL_GEN_HEADER)
|
||||
""")
|
||||
for func in functions:
|
||||
out.write(func["ret"]+" (GLAPIENTRY * "+func["name"]+")("+func["args"]+");\n")
|
||||
out.write("""
|
||||
#elif defined(AROPENGL_GEN_NAMES)
|
||||
|
||||
""")
|
||||
|
||||
for func in functions:
|
||||
out.write("\"gl"+func["name"]+"\\0\"\n")
|
||||
out.write("""
|
||||
#endif
|
||||
""")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1922
arlib/opengl/generated.c
Normal file
1922
arlib/opengl/generated.c
Normal file
File diff suppressed because it is too large
Load Diff
146
arlib/opengl/helpers.cpp
Normal file
146
arlib/opengl/helpers.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
#include "../arlib.h"
|
||||
|
||||
|
||||
const char gl_proc_names[] =
|
||||
#define AROPENGL_GEN_NAMES
|
||||
#include "generated.c"
|
||||
#undef AROPENGL_GEN_NAMES
|
||||
;
|
||||
|
||||
bool aropengl::create(context* core)
|
||||
{
|
||||
this->core = core;
|
||||
this->port = NULL;
|
||||
if (!core) return false;
|
||||
|
||||
const char * names = gl_proc_names;
|
||||
funcptr* out = (funcptr*)this;
|
||||
|
||||
while (*names)
|
||||
{
|
||||
*out = core->getProcAddress(names);
|
||||
out++;
|
||||
names += strlen(names)+1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Checks if needle is one of the space-separated words in the haystack. The needle may not contain spaces or be empty.
|
||||
static bool strtoken(const char * haystack, const char * needle)
|
||||
{
|
||||
//this is annoyingly complex to parse
|
||||
//I suspect 'people using fixed-size buffers, then extension list grows and app explodes' isn't the only reason the GL_EXTENSIONS string was deprecated
|
||||
int nlen = strlen(needle);
|
||||
while (true)
|
||||
{
|
||||
const char * found = strstr(haystack, needle);
|
||||
if (!found) break;
|
||||
|
||||
if ((found==haystack || found[-1]==' ') && // ensure the match is the start of a word
|
||||
(found[nlen]==' ' || found[nlen]=='\0')) // ensure the match is the end of a word
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
haystack = strchr(found, ' '); // try again, could've found GL_foobar_limited when looking for GL_foobar
|
||||
if (!haystack) return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
test()
|
||||
{
|
||||
assert(strtoken("aa", "aa"));
|
||||
assert(!strtoken("aa", "a"));
|
||||
assert(!strtoken("aa", "aaa"));
|
||||
assert(strtoken("aa aa aa aa", "aa"));
|
||||
assert(!strtoken("aa aa aa aa", "a"));
|
||||
assert(!strtoken("aa aa aa aa", "aaa"));
|
||||
assert(!strtoken("12345", "1234"));
|
||||
assert(!strtoken("12345", "2345"));
|
||||
assert(!strtoken("12345", "234"));
|
||||
assert(strtoken("1234 123456 2345 123456 0123456 012345 12345 12345", "12345"));
|
||||
assert(strtoken("a b b", "a"));
|
||||
assert(strtoken("b a b", "a"));
|
||||
assert(strtoken("b b a", "a"));
|
||||
}
|
||||
|
||||
bool aropengl::hasExtension(const char * ext)
|
||||
{
|
||||
int major = strtol((char*)this->GetString(GL_VERSION), NULL, 0);
|
||||
if (major >= 3)
|
||||
{
|
||||
GLint n;
|
||||
this->GetIntegerv(GL_NUM_EXTENSIONS, &n);
|
||||
for (GLint i=0;i<n;i++)
|
||||
{
|
||||
if (!strcmp((char*)this->GetStringi(GL_EXTENSIONS, i), ext)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return strtoken((char*)this->GetString(GL_EXTENSIONS), ext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void APIENTRY debug_cb(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char * message, void* userParam)
|
||||
{
|
||||
const char * source_s;
|
||||
const char * type_s;
|
||||
const char * severity_s;
|
||||
enum { sev_unk=255, sev_not=0, sev_warn=1, sev_err=2 } severity_l;
|
||||
|
||||
switch (source)
|
||||
{
|
||||
case GL_DEBUG_SOURCE_API: source_s="API"; break;
|
||||
case GL_DEBUG_SOURCE_WINDOW_SYSTEM: source_s="Window system"; break;
|
||||
case GL_DEBUG_SOURCE_SHADER_COMPILER: source_s="Shader compiler"; break;
|
||||
case GL_DEBUG_SOURCE_THIRD_PARTY: source_s="3rd party"; break;
|
||||
case GL_DEBUG_SOURCE_APPLICATION: source_s="Application"; break;
|
||||
case GL_DEBUG_SOURCE_OTHER: source_s="Other"; break;
|
||||
default: source_s="Unknown"; break;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GL_DEBUG_TYPE_ERROR: type_s="Error"; break;
|
||||
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: type_s="Deprecated behavior"; break;
|
||||
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: type_s="Undefined behavior"; break;
|
||||
case GL_DEBUG_TYPE_PORTABILITY: type_s="Portability"; break;
|
||||
case GL_DEBUG_TYPE_PERFORMANCE: type_s="Performance"; break;
|
||||
case GL_DEBUG_TYPE_MARKER: type_s="Marker"; break;
|
||||
case GL_DEBUG_TYPE_PUSH_GROUP: type_s="Push group"; break;
|
||||
case GL_DEBUG_TYPE_POP_GROUP: type_s="Pop group"; break;
|
||||
case GL_DEBUG_TYPE_OTHER: type_s="Other"; break;
|
||||
default: type_s="Unknown"; break;
|
||||
}
|
||||
|
||||
switch (severity)
|
||||
{
|
||||
case GL_DEBUG_SEVERITY_HIGH: severity_s="High"; severity_l=sev_err; break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM: severity_s="Medium"; severity_l=sev_warn; break;
|
||||
case GL_DEBUG_SEVERITY_LOW: severity_s="Low"; severity_l=sev_not; break;
|
||||
case GL_DEBUG_SEVERITY_NOTIFICATION: severity_s="Notice"; severity_l=sev_not; break;
|
||||
default: severity_s="Unknown"; severity_l=sev_unk; break;
|
||||
}
|
||||
|
||||
fprintf((FILE*)userParam, "[GL debug: sev %s, source %s, topic %s: %s]\n", severity_s, source_s, type_s, message);
|
||||
|
||||
if (severity_l >= sev_warn) debug_or_exit();
|
||||
}
|
||||
|
||||
void aropengl::enableDefaultDebugger(FILE* out)
|
||||
{
|
||||
if (!out) out = stderr;
|
||||
|
||||
this->DebugMessageCallback((GLDEBUGPROC)debug_cb, out);//some headers have 'const' on the userdata, some don't
|
||||
//https://www.opengl.org/sdk/docs/man/html/glDebugMessageCallback.xhtml says it shouldn't be const
|
||||
this->DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
|
||||
this->Enable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
|
||||
}
|
||||
119
arlib/opengl/test-gl.cpp
Normal file
119
arlib/opengl/test-gl.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
windows:
|
||||
g++ -DARLIB_D3DTEST -DARLIB_OPENGL -DARGUI_WINDOWS -DAROPENGL_D3DSYNC -std=c++11 -fno-exceptions -fno-rtti -O3 *.cpp ../os.cpp ../gui/*.cpp ../malloc.cpp ../file-win32.cpp -lgdi32 -lcomctl32 -lcomdlg32 -o test.exe && test.exe && del test.exe
|
||||
|
||||
linux:
|
||||
g++ -DARLIB_D3DTEST -DARLIB_OPENGL -DARGUI_GTK3 -DARGUIPROT_X11 -std=c++11 -fno-exceptions -fno-rtti -O3 *.cpp ../os.cpp ../gui/*.cpp ../malloc.cpp ../file-win32.cpp -ldl `pkg-config --cflags --libs gtk+-3.0` -lX11 -static-libgcc -o test
|
||||
*/
|
||||
|
||||
#ifdef ARLIB_D3DTEST
|
||||
//#define NTDDI_VERSION NTDDI_WS03
|
||||
//#define _WIN32_IE 0x0600
|
||||
//#include<windows.h> // must include this early, Arlib sets WIN32_LEAN_AND_MEAN which removes timeBeginPeriod
|
||||
|
||||
#include <time.h>
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
#include "../arlib.h"
|
||||
|
||||
void math(int* data, int ndata, float& avg, float& stddev)
|
||||
{
|
||||
if (!ndata)
|
||||
{
|
||||
avg=0;
|
||||
stddev=0;
|
||||
return;
|
||||
}
|
||||
|
||||
float sum = 0;
|
||||
for (int i=0;i<ndata;i++) sum+=data[i];
|
||||
avg = sum/ndata;
|
||||
|
||||
float stddevtmp = 0;
|
||||
for (int i=0;i<ndata;i++) stddevtmp += (data[i]-avg) * (data[i]-avg);
|
||||
stddev = sqrt(stddevtmp / ndata);
|
||||
}
|
||||
|
||||
void process(bool d3d)
|
||||
{
|
||||
widget_viewport* port = widget_create_viewport(300, 200);
|
||||
window* wnd = window_create(port);
|
||||
|
||||
uint32_t flags = aropengl::t_ver_3_3 | aropengl::t_debug_context;
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
if (d3d) flags |= aropengl::t_direct3d_vsync;
|
||||
#endif
|
||||
//flags |= aropengl::t_depth_buffer;
|
||||
aropengl gl(port, flags);
|
||||
if (!gl) return;
|
||||
|
||||
wnd->set_visible(true);
|
||||
|
||||
gl.enableDefaultDebugger();
|
||||
gl.swapInterval(1);
|
||||
|
||||
bool black = false;
|
||||
|
||||
//int width = 640;
|
||||
|
||||
#define SKIP 20
|
||||
#define FRAMES 1800
|
||||
int times[SKIP+FRAMES]={};
|
||||
|
||||
uint64_t prev = perfcounter();
|
||||
|
||||
for (int i=0;i<SKIP+FRAMES;i++)
|
||||
{
|
||||
window_run_iter();
|
||||
|
||||
black = !black;
|
||||
|
||||
gl.Viewport(0, 0, 640, 480);
|
||||
gl.ClearColor(black, 1-black, 0, 1.0);
|
||||
gl.Clear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
gl.swapBuffers();
|
||||
|
||||
//width++;
|
||||
//if(width>1000)width=500;
|
||||
//port->resize(width, 480);
|
||||
//gl.notifyResize(width, 480);
|
||||
|
||||
uint64_t now = perfcounter();
|
||||
times[i] = now-prev;
|
||||
prev = now;
|
||||
}
|
||||
|
||||
delete wnd;
|
||||
|
||||
float avg;
|
||||
float stddev;
|
||||
math(times+SKIP, FRAMES, avg, stddev);
|
||||
|
||||
printf("d3d=%i avg=%f stddev=%f ", d3d, avg, stddev);
|
||||
std::sort(times+SKIP, times+SKIP+FRAMES);
|
||||
printf("min=%i,%i ", times[SKIP+0], times[SKIP+1]);
|
||||
printf("max=%i,%i,%i,%i,%i\n", times[SKIP+FRAMES-1], times[SKIP+FRAMES-2], times[SKIP+FRAMES-3], times[SKIP+FRAMES-4], times[SKIP+FRAMES-5]);
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
window_init(&argc, &argv);
|
||||
//timeBeginPeriod(1);
|
||||
//this is supposed to measure how much D3D sync helps, but either
|
||||
//- there's nonzero time between SwapBuffers/PresentEx return and the frame is presented
|
||||
//- DWM and/or the driver has determined that I want high-quality vsync, and auto enables it
|
||||
//- I'm measuring wrong thing
|
||||
//- the mere act of mentioning d3d9.dll in the binary scares it into submission
|
||||
//because I can't measure any meaningful difference whatsoever.
|
||||
//When I first got this working, I had std.dev 4000us for pure GL and 250 for D3D sync, what happened?
|
||||
|
||||
for (int i=0;i<5;i++)
|
||||
{
|
||||
process(false);
|
||||
#ifdef AROPENGL_D3DSYNC
|
||||
process(true);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
211
arlib/os.cpp
Normal file
211
arlib/os.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
#include "os.h"
|
||||
#include "thread.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __unix__
|
||||
#include <dlfcn.h>
|
||||
|
||||
static mutex dylib_lock;
|
||||
|
||||
bool dylib::init(const char * filename, bool * owned)
|
||||
{
|
||||
deinit();
|
||||
|
||||
dylib_lock.lock();
|
||||
|
||||
if (owned)
|
||||
{
|
||||
handle = (dylib*)dlopen(filename, RTLD_LAZY|RTLD_NOLOAD);
|
||||
*owned = (!handle);
|
||||
}
|
||||
|
||||
if (!handle) handle = (dylib*)dlopen(filename, RTLD_LAZY);
|
||||
|
||||
dylib_lock.unlock();
|
||||
return handle;
|
||||
}
|
||||
|
||||
void* dylib::sym_ptr(const char * name)
|
||||
{
|
||||
if (!handle) return NULL;
|
||||
return dlsym(handle, name);
|
||||
}
|
||||
|
||||
funcptr dylib::sym_func(const char * name)
|
||||
{
|
||||
if (!handle) return NULL;
|
||||
|
||||
funcptr ret;
|
||||
*(void**)(&ret)=dlsym(handle, name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dylib::deinit()
|
||||
{
|
||||
if (handle) dlclose(handle);
|
||||
handle = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
static mutex dylib_lock;
|
||||
|
||||
bool dylib::init(const char * filename, bool * owned)
|
||||
{
|
||||
deinit();
|
||||
|
||||
dylib_lock.lock();
|
||||
|
||||
if (owned)
|
||||
{
|
||||
if (!GetModuleHandleEx(0, filename, (HMODULE*)&handle)) handle=NULL;
|
||||
*owned=(!handle);
|
||||
}
|
||||
|
||||
if (!handle)
|
||||
{
|
||||
//this is so weird dependencies, for example winpthread-1.dll, can be placed beside the dll where they belong
|
||||
char * filename_copy=strdup(filename);
|
||||
char * filename_copy_slash = strrchr(filename_copy, '/');
|
||||
if (!filename_copy_slash) filename_copy_slash = strrchr(filename_copy, '\0');
|
||||
filename_copy_slash[0]='\0';
|
||||
SetDllDirectory(filename_copy);
|
||||
free(filename_copy);
|
||||
|
||||
handle = (dylib*)LoadLibrary(filename);
|
||||
SetDllDirectory(NULL);
|
||||
}
|
||||
|
||||
dylib_lock.unlock();
|
||||
return handle;
|
||||
}
|
||||
|
||||
void* dylib::sym_ptr(const char * name)
|
||||
{
|
||||
if (!handle) return NULL;
|
||||
return (void*)GetProcAddress((HMODULE)handle, name);
|
||||
}
|
||||
|
||||
funcptr dylib::sym_func(const char * name)
|
||||
{
|
||||
if (!handle) return NULL;
|
||||
return (funcptr)GetProcAddress((HMODULE)handle, name);
|
||||
}
|
||||
|
||||
void dylib::deinit()
|
||||
{
|
||||
if (handle) FreeLibrary((HMODULE)handle);
|
||||
handle = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool dylib::sym_multi(funcptr* out, const char * names)
|
||||
{
|
||||
bool all = true;
|
||||
|
||||
while (*names)
|
||||
{
|
||||
*out = this->sym_func(names);
|
||||
if (!*out) all = false;
|
||||
|
||||
out++;
|
||||
names += strlen(names)+1;
|
||||
}
|
||||
|
||||
return all;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
void debug_or_ignore()
|
||||
{
|
||||
if (IsDebuggerPresent()) DebugBreak();
|
||||
}
|
||||
|
||||
void debug_or_exit()
|
||||
{
|
||||
if (IsDebuggerPresent()) DebugBreak();
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
void debug_or_abort()
|
||||
{
|
||||
DebugBreak();
|
||||
FatalExit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __unix__
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
//method from https://src.chromium.org/svn/trunk/src/base/debug/debugger_posix.cc
|
||||
static bool has_debugger()
|
||||
{
|
||||
char buf[4096];
|
||||
int fd = open("/proc/self/status", O_RDONLY);
|
||||
if (!fd) return false;
|
||||
|
||||
ssize_t bytes = read(fd, buf, sizeof(buf)-1);
|
||||
close(fd);
|
||||
|
||||
if (bytes < 0) return false;
|
||||
buf[bytes] = '\0';
|
||||
|
||||
const char * tracer = strstr(buf, "TracerPid:\t");
|
||||
if (!tracer) return false;
|
||||
tracer += strlen("TracerPid:\t");
|
||||
|
||||
return (*tracer != '0');
|
||||
}
|
||||
|
||||
void debug_or_ignore()
|
||||
{
|
||||
if (has_debugger()) raise(SIGTRAP);
|
||||
}
|
||||
|
||||
void debug_or_exit()
|
||||
{
|
||||
if (has_debugger()) raise(SIGTRAP);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void debug_or_abort()
|
||||
{
|
||||
raise(SIGTRAP);
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
uint64_t perfcounter()
|
||||
{
|
||||
////this one has an accuracy of 10ms by default
|
||||
//ULARGE_INTEGER time;
|
||||
//GetSystemTimeAsFileTime((LPFILETIME)&time);
|
||||
//return time.QuadPart/10;//this one is in intervals of 100 nanoseconds, for some insane reason. We want microseconds.
|
||||
|
||||
static LARGE_INTEGER timer_freq;
|
||||
if (!timer_freq.QuadPart) QueryPerformanceFrequency(&timer_freq);
|
||||
|
||||
LARGE_INTEGER timer_now;
|
||||
QueryPerformanceCounter(&timer_now);
|
||||
return 1000000*timer_now.QuadPart/timer_freq.QuadPart;
|
||||
}
|
||||
#else
|
||||
#include <time.h>
|
||||
|
||||
uint64_t perfcounter()
|
||||
{
|
||||
struct timespec tp;
|
||||
clock_gettime(CLOCK_MONOTONIC, &tp);
|
||||
return tp.tv_sec*1000000 + tp.tv_nsec/1000;
|
||||
}
|
||||
#endif
|
||||
41
arlib/os.h
41
arlib/os.h
@@ -1,9 +1,6 @@
|
||||
#pragma once
|
||||
#include "global.h"
|
||||
|
||||
//this is more thread.h than anything else - dylib is the only non-thread-related part. But there's
|
||||
// no other place where dylib would fit, so os.h it is.
|
||||
|
||||
#ifdef __unix__
|
||||
#define DYLIB_EXT ".so"
|
||||
#define DYLIB_MAKE_NAME(name) "lib" name DYLIB_EXT
|
||||
@@ -17,22 +14,36 @@
|
||||
//The size varies per platform, so I have to allocate the object. This could be done by putting in a void* member,
|
||||
// but that's a pointless level of indirection - instead, I cast the allocated value and return that!
|
||||
//It's probably undefined, but the compiler won't be able to prove that, so it has to do what I want.
|
||||
//Perhaps it would be better to let the configure script declare what the size is so they can have a
|
||||
// member of type uint32_t data[12] and be constructed normally, but this is good enough for now.
|
||||
class dylib : private nocopy {
|
||||
dylib(){}
|
||||
class dylib : nocopy {
|
||||
void* handle;
|
||||
|
||||
public:
|
||||
static dylib* create(const char * filename, bool * owned=NULL);
|
||||
static const char * ext() { return DYLIB_EXT; }
|
||||
dylib() { handle=NULL; }
|
||||
dylib(const char * filename) { handle=NULL; init(filename); }
|
||||
|
||||
//owned tells whether the DLL was loaded before calling this
|
||||
//this is an atomic operation; if multiple threads call dylib::create for the same file, only one will get owned==true
|
||||
//init() may only be called once
|
||||
bool init(const char * filename, bool * owned=NULL);
|
||||
void* sym_ptr(const char * name);
|
||||
funcptr sym_func(const char * name);
|
||||
template<typename T> T sym(const char * name) { return (T)sym_func(name); }
|
||||
|
||||
//per http://chadaustin.me/cppinterface.html - redirect operator delete to a function, this doesn't come from the normal allocator.
|
||||
static void operator delete(void* p) { if (p) ((dylib*)p)->release(); }
|
||||
void release();//this is the real destructor, you can use either this one or delete it
|
||||
//Fetches multiple symbols. 'names' is expected to be a NUL-separated list of names, terminated with a blank one.
|
||||
// (You don't need to do anything special to create this terminator. Just use the NUL terminator the compiler adds.)
|
||||
//Returns whether all of them were successfully fetched. Failures are NULL.
|
||||
bool sym_multi(funcptr* out, const char * names);
|
||||
|
||||
void deinit();
|
||||
~dylib() { deinit(); }
|
||||
};
|
||||
|
||||
//If the program is run under a debugger, this triggers a breakpoint. If not, ignored.
|
||||
void debug_break();
|
||||
//If the program is run under a debugger, this triggers a breakpoint. The program is then terminated.
|
||||
void debug_abort();
|
||||
void debug_or_ignore();
|
||||
//If the program is run under a debugger, this triggers a breakpoint. If not, the program silently exits.
|
||||
void debug_or_exit();
|
||||
//If the program is run under a debugger, this triggers a breakpoint. If not, the program crashes.
|
||||
void debug_or_abort();
|
||||
|
||||
//Returns time since an undefined point in time, in microseconds. The epoch may vary across machines or reboots.
|
||||
uint64_t perfcounter();
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
#include "sandbox/sandbox.h"
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include "../global.h"
|
||||
#include "../string.h"
|
||||
|
||||
#ifdef ARLIB_SANDBOX
|
||||
//Allows safely executing untrusted code.
|
||||
//
|
||||
//Exact rules:
|
||||
@@ -32,8 +32,9 @@
|
||||
// are well documented, and if I miss something, strace quickly tells me what.
|
||||
//
|
||||
//On Linux, this requires exclusive control over SIGSYS in the child process.
|
||||
//All uses (according to <http://lxr.free-electrons.com/ident?i=SIGSYS>, kernel version 4.6) are either seccomp,
|
||||
//According to <http://lxr.free-electrons.com/ident?i=SIGSYS>, kernel version 4.6, all uses are either seccomp,
|
||||
// hardware events on rare platforms that won't be delivered to me, or catching / passing on the signal (as opposed to raising it).
|
||||
// kill(2) can, of course, also send SIGSYS, but that's rare.
|
||||
//Therefore, this requirement is safe.
|
||||
//
|
||||
//Chrome sandbox entry points: http://stackoverflow.com/questions/1590337/using-the-google-chrome-sandbox
|
||||
@@ -52,9 +53,9 @@ public:
|
||||
//If true, stdout and stderr go to the same places as in the parent. If false, /dev/null.
|
||||
allow_stdout = 1,
|
||||
|
||||
//Creates a sandbox facade; it acts like a normal sandbox, but the child process isn't
|
||||
// restricted. It could even be a thread in the same process.
|
||||
no_security = 2,
|
||||
////Creates a sandbox facade; it acts like a normal sandbox, but the child process isn't
|
||||
//// restricted. It could even be a thread in the same process.
|
||||
//no_security = 2,
|
||||
};
|
||||
unsigned int flags;
|
||||
|
||||
@@ -96,7 +97,7 @@ public:
|
||||
bool try_lock() { return parent->try_wait(id); }
|
||||
void unlock() { parent->release(id); }
|
||||
};
|
||||
channel_t channel(int id) { return (channel_t){this, id}; }
|
||||
channel_t channel(int id) { channel_t ret; ret.parent=this; ret.id=id; return ret; }
|
||||
|
||||
//Allocates memory shared between the two processes. At least 8 memory areas are supported. It's
|
||||
// up to the user how to use them; a recommendation is to put a fixed-size control data block in
|
||||
@@ -123,7 +124,7 @@ public:
|
||||
//If the return value from this callback is not equal to -1, that will be returned as a file handle.
|
||||
//It is safe to call accept_fd() from this function.
|
||||
//On Windows, the intptr_t is a casted HANDLE. On Linux, int.
|
||||
void set_fopen_fallback(function<intptr_t(const char * path, bool write)> callback); // Child only.
|
||||
void set_fopen_fallback(function<intptr_t(cstring path, bool write)> callback); // Child only.
|
||||
|
||||
//Clones a file handle into the child. The handle remains open in the parent. The child may get another ID.
|
||||
//Like shalloc(), both processes are allowed to sleep until the other enters.
|
||||
@@ -142,4 +143,3 @@ private:
|
||||
sandbox(){}
|
||||
sandbox(impl* m) : m(m) {}
|
||||
};
|
||||
#endif
|
||||
|
||||
98
arlib/serialize-test.cpp
Normal file
98
arlib/serialize-test.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include "serialize.h"
|
||||
#include "test.h"
|
||||
|
||||
#ifdef ARLIB_TEST
|
||||
struct ser1 {
|
||||
int a;
|
||||
int b;
|
||||
|
||||
SERIALIZE(a, b);
|
||||
};
|
||||
|
||||
struct ser2 {
|
||||
ser1 c;
|
||||
ser1 d;
|
||||
|
||||
SERIALIZE(c, d);
|
||||
};
|
||||
|
||||
struct ser3 {
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
int e;
|
||||
int f;
|
||||
int g;
|
||||
int h;
|
||||
|
||||
SERIALIZE(a, b, c, d, e, f, g, h);
|
||||
};
|
||||
|
||||
struct ser4 {
|
||||
ser3 mem;
|
||||
int count = 0;
|
||||
template<typename T> void serialize(T& s) { mem.serialize(s); count++; }
|
||||
};
|
||||
|
||||
test()
|
||||
{
|
||||
{
|
||||
ser1 item;
|
||||
item.a = 1;
|
||||
item.b = 2;
|
||||
|
||||
assert_eq(bmlserialize(item), "a=1\nb=2");
|
||||
}
|
||||
|
||||
{
|
||||
ser2 item;
|
||||
item.c.a = 1;
|
||||
item.c.b = 2;
|
||||
item.d.a = 3;
|
||||
item.d.b = 4;
|
||||
assert_eq(bmlserialize(item), "c a=1 b=2\nd a=3 b=4");
|
||||
}
|
||||
}
|
||||
|
||||
test()
|
||||
{
|
||||
{
|
||||
ser1 item = bmlunserialize<ser1>("a=1\nb=2");
|
||||
assert_eq(item.a, 1);
|
||||
assert_eq(item.b, 2);
|
||||
}
|
||||
|
||||
{
|
||||
ser2 item = bmlunserialize<ser2>("c a=1 b=2\nd a=3 b=4");
|
||||
assert_eq(item.c.a, 1);
|
||||
assert_eq(item.c.b, 2);
|
||||
assert_eq(item.d.a, 3);
|
||||
assert_eq(item.d.b, 4);
|
||||
}
|
||||
|
||||
//the system should not be order-sensitive
|
||||
{
|
||||
ser2 item = bmlunserialize<ser2>("d b=4 a=3\nc a=1 b=2");
|
||||
assert_eq(item.c.a, 1);
|
||||
assert_eq(item.c.b, 2);
|
||||
assert_eq(item.d.a, 3);
|
||||
assert_eq(item.d.b, 4);
|
||||
}
|
||||
|
||||
//in case of dupes, last one should win; extraneous nodes should be cleanly ignored
|
||||
{
|
||||
ser1 item = bmlunserialize<ser1>("a=1\nb=2\nq=0\na=3\na=4");
|
||||
assert_eq(item.a, 4);
|
||||
assert_eq(item.b, 2);
|
||||
}
|
||||
|
||||
//the system is allowed to loop, but only if there's bogus or extraneous nodes
|
||||
//we want O(n) runtime for a clean document, so ensure no looping
|
||||
//this includes missing and duplicate elements, both of which are possible for serialized arrays
|
||||
{
|
||||
ser4 item = bmlunserialize<ser4>("a=1\nb=2\nd=4\ne=5\ne=5\nf=6");
|
||||
assert_eq(item.count, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,251 +0,0 @@
|
||||
#include "serialize.h"
|
||||
#include "test.h"
|
||||
#include "bml.h"
|
||||
|
||||
/*
|
||||
|
||||
{ a; b; }
|
||||
a=1
|
||||
b=2
|
||||
|
||||
{ a={ b; c; } d; }
|
||||
a
|
||||
b=1
|
||||
c=2
|
||||
d=3
|
||||
|
||||
*/
|
||||
|
||||
#define SERIALIZE_CORE(member) s(STR(member), member);
|
||||
#define SERIALIZE(...) template<typename T> void serialize(T& s) { PPFOREACH(SERIALIZE_CORE, __VA_ARGS__); }
|
||||
|
||||
class bmlserialize_impl {
|
||||
bmlwriter w;
|
||||
template<typename T> friend string bmlserialize(T& item);
|
||||
|
||||
public:
|
||||
|
||||
static const bool serializing = true;
|
||||
|
||||
template<typename T> void operator()(cstring name, T& item)
|
||||
{
|
||||
w.enter(name, "");
|
||||
item.serialize(*this);
|
||||
w.exit();
|
||||
}
|
||||
|
||||
#define LEAF(T) void operator()(cstring name, T& item) { w.node(name, tostring(item)); }
|
||||
LEAF(char);
|
||||
LEAF(int);
|
||||
LEAF(unsigned int);
|
||||
LEAF(bool);
|
||||
LEAF(float);
|
||||
LEAF(time_t);
|
||||
#undef LEAF
|
||||
};
|
||||
|
||||
template<typename T> string bmlserialize(T& item)
|
||||
{
|
||||
bmlserialize_impl s;
|
||||
item.serialize(s);
|
||||
return s.w.finish();
|
||||
}
|
||||
|
||||
|
||||
|
||||
class bmlunserialize_impl {
|
||||
bmlparser p;
|
||||
int pdepth = 0;
|
||||
|
||||
int thisdepth = 0;
|
||||
cstring thisnode;
|
||||
cstring thisval;
|
||||
bool matchagain;
|
||||
|
||||
bmlparser::event event()
|
||||
{
|
||||
bmlparser::event ret = p.next();
|
||||
if (ret.action == bmlparser::enter) pdepth++;
|
||||
if (ret.action == bmlparser::exit) pdepth--;
|
||||
if (ret.action == bmlparser::finish) pdepth=-2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void skipchildren()
|
||||
{
|
||||
while (pdepth > thisdepth) event();
|
||||
}
|
||||
|
||||
bmlunserialize_impl(cstring bml) : p(bml) {}
|
||||
template<typename T> friend T bmlunserialize(cstring bml);
|
||||
|
||||
template<typename T> void item(T& out)
|
||||
{
|
||||
while (pdepth >= thisdepth)
|
||||
{
|
||||
bmlparser::event ev = event();
|
||||
if (ev.action == bmlparser::enter)
|
||||
{
|
||||
thisdepth++;
|
||||
thisnode = ev.name;
|
||||
thisval = ev.value;
|
||||
do {
|
||||
matchagain = false;
|
||||
out.serialize(*this);
|
||||
} while (matchagain);
|
||||
thisdepth--;
|
||||
skipchildren();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void next()
|
||||
{
|
||||
matchagain = false;
|
||||
|
||||
if (pdepth >= thisdepth)
|
||||
{
|
||||
thisdepth--;
|
||||
skipchildren();
|
||||
|
||||
bmlparser::event ev = event();
|
||||
if (ev.action == bmlparser::enter)
|
||||
{
|
||||
matchagain = true;
|
||||
thisnode = ev.name;
|
||||
thisval = ev.value;
|
||||
}
|
||||
|
||||
thisdepth++;
|
||||
}
|
||||
}
|
||||
|
||||
#define LEAF(T) void item(T& out) { out = fromstring<T>(thisval); }
|
||||
LEAF(char);
|
||||
LEAF(int);
|
||||
LEAF(unsigned int);
|
||||
LEAF(bool);
|
||||
LEAF(float);
|
||||
LEAF(time_t);
|
||||
#undef LEAF
|
||||
|
||||
public:
|
||||
|
||||
static const bool serializing = false;
|
||||
|
||||
template<typename T> void operator()(cstring name, T& out)
|
||||
{
|
||||
while (thisnode == name) // this should be a loop, in case of documents like 'foo bar=1 bar=2 bar=3'
|
||||
{
|
||||
item(out);
|
||||
thisnode = "";
|
||||
next();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> T bmlunserialize(cstring bml)
|
||||
{
|
||||
T out{};
|
||||
bmlunserialize_impl s(bml);
|
||||
s.item(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef ARLIB_TEST
|
||||
struct ser1 {
|
||||
int a;
|
||||
int b;
|
||||
|
||||
SERIALIZE(a, b);
|
||||
};
|
||||
|
||||
struct ser2 {
|
||||
ser1 c;
|
||||
ser1 d;
|
||||
|
||||
SERIALIZE(c, d);
|
||||
};
|
||||
|
||||
struct ser3 {
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
int e;
|
||||
int f;
|
||||
int g;
|
||||
int h;
|
||||
|
||||
SERIALIZE(a, b, c, d, e, f, g, h);
|
||||
};
|
||||
|
||||
struct ser4 {
|
||||
ser3 mem;
|
||||
int count = 0;
|
||||
template<typename T> void serialize(T& s) { mem.serialize(s); count++; }
|
||||
};
|
||||
|
||||
test()
|
||||
{
|
||||
{
|
||||
ser1 item;
|
||||
item.a = 1;
|
||||
item.b = 2;
|
||||
|
||||
assert_eq(bmlserialize(item), "a=1\nb=2");
|
||||
}
|
||||
|
||||
{
|
||||
ser2 item;
|
||||
item.c.a = 1;
|
||||
item.c.b = 2;
|
||||
item.d.a = 3;
|
||||
item.d.b = 4;
|
||||
assert_eq(bmlserialize(item), "c a=1 b=2\nd a=3 b=4");
|
||||
}
|
||||
}
|
||||
|
||||
test()
|
||||
{
|
||||
{
|
||||
ser1 item = bmlunserialize<ser1>("a=1\nb=2");
|
||||
assert_eq(item.a, 1);
|
||||
assert_eq(item.b, 2);
|
||||
}
|
||||
|
||||
{
|
||||
ser2 item = bmlunserialize<ser2>("c a=1 b=2\nd a=3 b=4");
|
||||
assert_eq(item.c.a, 1);
|
||||
assert_eq(item.c.b, 2);
|
||||
assert_eq(item.d.a, 3);
|
||||
assert_eq(item.d.b, 4);
|
||||
}
|
||||
|
||||
//the system should not be order-sensitive
|
||||
{
|
||||
ser2 item = bmlunserialize<ser2>("d b=4 a=3\nc a=1 b=2");
|
||||
assert_eq(item.c.a, 1);
|
||||
assert_eq(item.c.b, 2);
|
||||
assert_eq(item.d.a, 3);
|
||||
assert_eq(item.d.b, 4);
|
||||
}
|
||||
|
||||
//in case of dupes, last one should win; extraneous nodes should be cleanly ignored
|
||||
{
|
||||
ser1 item = bmlunserialize<ser1>("a=1\nb=2\nq=0\na=3\na=4");
|
||||
assert_eq(item.a, 4);
|
||||
assert_eq(item.b, 2);
|
||||
}
|
||||
|
||||
//the system is allowed to loop, but only if there's bogus or extraneous nodes
|
||||
//we want O(n) runtime for a clean document, so ensure no looping
|
||||
//this includes missing and duplicate elements, both of which are possible for serialized arrays
|
||||
{
|
||||
ser4 item = bmlunserialize<ser4>("a=1\nb=2\nd=4\ne=5\ne=5\nf=6");
|
||||
assert_eq(item.count, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,3 +1,130 @@
|
||||
#pragma once
|
||||
#include "global.h"
|
||||
#include "bml.h"
|
||||
#include "stringconv.h"
|
||||
|
||||
#define SERIALIZE_CORE(member) s(STR(member), member);
|
||||
#define SERIALIZE(...) template<typename T> void serialize(T& s) { PPFOREACH(SERIALIZE_CORE, __VA_ARGS__); }
|
||||
|
||||
class bmlserialize_impl {
|
||||
bmlwriter w;
|
||||
template<typename T> friend string bmlserialize(T& item);
|
||||
|
||||
public:
|
||||
|
||||
static const bool serializing = true;
|
||||
|
||||
template<typename T> void operator()(cstring name, T& item)
|
||||
{
|
||||
w.enter(name, "");
|
||||
item.serialize(*this);
|
||||
w.exit();
|
||||
}
|
||||
|
||||
#define LEAF(T) void operator()(cstring name, T& item) { w.node(name, tostring(item)); }
|
||||
ALLSTRINGABLE(LEAF);
|
||||
#undef LEAF
|
||||
};
|
||||
|
||||
template<typename T> string bmlserialize(T& item)
|
||||
{
|
||||
bmlserialize_impl s;
|
||||
item.serialize(s);
|
||||
return s.w.finish();
|
||||
}
|
||||
|
||||
|
||||
|
||||
class bmlunserialize_impl {
|
||||
bmlparser p;
|
||||
int pdepth = 0;
|
||||
|
||||
int thisdepth = 0;
|
||||
cstring thisnode;
|
||||
cstring thisval;
|
||||
bool matchagain;
|
||||
|
||||
bmlparser::event event()
|
||||
{
|
||||
bmlparser::event ret = p.next();
|
||||
if (ret.action == bmlparser::enter) pdepth++;
|
||||
if (ret.action == bmlparser::exit) pdepth--;
|
||||
if (ret.action == bmlparser::finish) pdepth=-2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void skipchildren()
|
||||
{
|
||||
while (pdepth > thisdepth) event();
|
||||
}
|
||||
|
||||
bmlunserialize_impl(cstring bml) : p(bml) {}
|
||||
template<typename T> friend T bmlunserialize(cstring bml);
|
||||
|
||||
template<typename T> void item(T& out)
|
||||
{
|
||||
while (pdepth >= thisdepth)
|
||||
{
|
||||
bmlparser::event ev = event();
|
||||
if (ev.action == bmlparser::enter)
|
||||
{
|
||||
thisdepth++;
|
||||
thisnode = ev.name;
|
||||
thisval = ev.value;
|
||||
do {
|
||||
matchagain = false;
|
||||
out.serialize(*this);
|
||||
} while (matchagain);
|
||||
thisdepth--;
|
||||
skipchildren();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void next()
|
||||
{
|
||||
matchagain = false;
|
||||
|
||||
if (pdepth >= thisdepth)
|
||||
{
|
||||
thisdepth--;
|
||||
skipchildren();
|
||||
|
||||
bmlparser::event ev = event();
|
||||
if (ev.action == bmlparser::enter)
|
||||
{
|
||||
matchagain = true;
|
||||
thisnode = ev.name;
|
||||
thisval = ev.value;
|
||||
}
|
||||
|
||||
thisdepth++;
|
||||
}
|
||||
}
|
||||
|
||||
#define LEAF(T) void item(T& out) { fromstring(thisval, out); }
|
||||
ALLSTRINGABLE(LEAF);
|
||||
#undef LEAF
|
||||
|
||||
public:
|
||||
|
||||
static const bool serializing = false;
|
||||
|
||||
template<typename T> void operator()(cstring name, T& out)
|
||||
{
|
||||
while (thisnode == name) // this should be a loop, in case of documents like 'foo bar=1 bar=2 bar=3'
|
||||
{
|
||||
item(out);
|
||||
thisnode = "";
|
||||
next();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> T bmlunserialize(cstring bml)
|
||||
{
|
||||
T out{};
|
||||
bmlunserialize_impl s(bml);
|
||||
s.item(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
#include "socket/socket.h"
|
||||
92
arlib/socket/shitty-server.c
Normal file
92
arlib/socket/shitty-server.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Shitty Server: an unusually agressive Discard Protocol server <https://en.wikipedia.org/wiki/Discard_Protocol>
|
||||
* it reads and discards all your data for at least 5 seconds; then it drops your connection on the floor, without FIN or anything
|
||||
* probably somewhat useful to test your program's resilience against network failure
|
||||
*
|
||||
* system requirements: linux only, requires root because TCP_REPAIR requires that
|
||||
* if you need to test a windows program against dropped sockets, run this on another machine, possibly a virtual machine
|
||||
*
|
||||
* license: WTFPL, any version
|
||||
*
|
||||
* further reading: http://oroboro.com/dealing-with-network-port-abuse-in-sockets-in-c
|
||||
*/
|
||||
|
||||
/* feel free to replace with port 99 if you want to test in firefox or something */
|
||||
/* (no idea why they'd block the port specifically defined to not parse your data) */
|
||||
#define PORTNR 9
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
int listen_create(int port)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
sa.sin_port = htons(port);
|
||||
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
bind(fd, (struct sockaddr*)&sa, sizeof(sa));
|
||||
listen(fd, 10);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int* fds = NULL;
|
||||
int nfds_prev = 0; /* whenever nextcycle arrives, discard this many sockets from the array */
|
||||
int nfds = 0;
|
||||
time_t nextcycle = time(NULL);
|
||||
|
||||
int listen = listen_create(PORTNR);
|
||||
|
||||
while (1)
|
||||
{
|
||||
int i;
|
||||
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(listen, &fdset);
|
||||
for (i=0;i<nfds;i++) FD_SET(fds[i], &fdset);
|
||||
select(FD_SETSIZE, &fdset, NULL, NULL, NULL); /* don't bother with timeouts */
|
||||
|
||||
if (FD_ISSET(listen, &fdset))
|
||||
{
|
||||
int newfd = accept4(listen, NULL, NULL, SOCK_NONBLOCK);
|
||||
if (newfd >= 0)
|
||||
{
|
||||
fds = realloc(fds, sizeof(int)*(nfds+1));
|
||||
fds[nfds] = newfd;
|
||||
nfds++;
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0;i<nfds;i++)
|
||||
{
|
||||
if (FD_ISSET(fds[i], &fdset))
|
||||
{
|
||||
static char dump[1024];
|
||||
recv(fds[i], dump, sizeof(dump), MSG_DONTWAIT);
|
||||
}
|
||||
}
|
||||
|
||||
if (time(NULL) > nextcycle)
|
||||
{
|
||||
nextcycle = time(NULL)+5;
|
||||
for (i=0;i<nfds_prev;i++)
|
||||
{
|
||||
static int yes = 1;
|
||||
setsockopt(fds[i], IPPROTO_TCP, TCP_REPAIR, &yes, sizeof(yes));
|
||||
close(fds[i]);
|
||||
}
|
||||
memmove(fds, fds+nfds_prev, sizeof(int)*(nfds-nfds_prev));
|
||||
nfds -= nfds_prev;
|
||||
nfds_prev = nfds;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
#ifdef ARLIB_TEST_SERVER
|
||||
//Shitty Server: a buggy echo server
|
||||
//after the first 32 bytes, it drops your connection on the floor, without FIN or anything
|
||||
//probably somewhat useful to test resilience against network failure
|
||||
//it would be more useful to make it ignore the pings too, but I can't do that without fiddling with the firewall, and I'd rather not
|
||||
|
||||
//linux and root only because TCP_REPAIR requires that
|
||||
//http://oroboro.com/dealing-with-network-port-abuse-in-sockets-in-c
|
||||
//if you need to test a windows program against dropped sockets, run this on another machine, possibly a virtual machine
|
||||
|
||||
//most of the code stolen from http://www.thegeekstuff.com/2011/12/c-socket-programming/ because I'm lazy
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int listenfd = 0, connfd = 0;
|
||||
struct sockaddr_in serv_addr;
|
||||
|
||||
char sendBuff[1025];
|
||||
time_t ticks;
|
||||
|
||||
listenfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
memset(&serv_addr, '0', sizeof(serv_addr));
|
||||
memset(sendBuff, '0', sizeof(sendBuff));
|
||||
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
serv_addr.sin_port = htons(168);
|
||||
|
||||
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
|
||||
perror("bind");
|
||||
|
||||
listen(listenfd, 10);
|
||||
perror("listen");
|
||||
|
||||
while(1)
|
||||
{
|
||||
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
|
||||
perror("accept");
|
||||
|
||||
memset(sendBuff, 0, 32);
|
||||
read(connfd, sendBuff, 32);
|
||||
write(connfd, sendBuff, 32);
|
||||
sleep(1); // otherwise the ACK gives a RST
|
||||
|
||||
int yes = 1;
|
||||
setsockopt(connfd, SOL_TCP, TCP_REPAIR, &yes, sizeof(yes));
|
||||
perror("TCP_REPAIR");
|
||||
|
||||
close(connfd);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
165
arlib/socket/socket-gnutls.cpp
Normal file
165
arlib/socket/socket-gnutls.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
#include "socket.h"
|
||||
|
||||
#ifdef ARLIB_SSL_GNUTLS
|
||||
//could use the gnutls C++ api, but why should I
|
||||
//extra dependency, and the added convenience isn't relevant on such a small component
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/x509.h>
|
||||
|
||||
//based on http://www.gnutls.org/manual/html_node/Legacy-client-example-with-X_002e509-certificate-support.html
|
||||
|
||||
static gnutls_certificate_credentials_t xcred;
|
||||
static bool init_ok = false;
|
||||
|
||||
#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
|
||||
static int _verify_certificate_callback(gnutls_session_t session);
|
||||
|
||||
static void initialize()
|
||||
{
|
||||
static bool initialized = false;
|
||||
if (initialized) return;
|
||||
|
||||
#define CHECK(x) if ((x)<0) return
|
||||
CHECK(gnutls_check_version("3.1.4") != NULL);
|
||||
|
||||
CHECK(gnutls_global_init());
|
||||
|
||||
CHECK(gnutls_certificate_allocate_credentials(&xcred));
|
||||
CHECK(gnutls_certificate_set_x509_trust_file(xcred, CAFILE, GNUTLS_X509_FMT_PEM));
|
||||
gnutls_certificate_set_verify_function(xcred, _verify_certificate_callback);
|
||||
#undef CHECK
|
||||
|
||||
init_ok = true;
|
||||
}
|
||||
//to deinit:
|
||||
//gnutls_certificate_free_credentials(xcred);
|
||||
//gnutls_global_deinit();
|
||||
|
||||
static int _verify_certificate_callback(gnutls_session_t session)
|
||||
{
|
||||
#define CHECK(x) if ((x)<0) return GNUTLS_E_CERTIFICATE_ERROR
|
||||
const char * hostname = (char*)gnutls_session_get_ptr(session);
|
||||
if (!hostname) return 0;
|
||||
|
||||
unsigned int status;
|
||||
CHECK(gnutls_certificate_verify_peers3(session, hostname, &status));
|
||||
|
||||
//gnutls_certificate_type_t type = gnutls_certificate_type_get(session);
|
||||
//gnutls_datum_t out;
|
||||
//CHECK(gnutls_certificate_verification_status_print(status, type,
|
||||
// &out, 0));
|
||||
//printf("%s", out.data);
|
||||
//gnutls_free(out.data);
|
||||
|
||||
if (status != 0) return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
return 0;
|
||||
#undef CHECK
|
||||
}
|
||||
|
||||
class socketssl_impl : public socketssl {
|
||||
public:
|
||||
socket* sock;
|
||||
gnutls_session_t session;
|
||||
bool block;
|
||||
|
||||
|
||||
bool init(socket* parent, cstring domain, bool permissive)
|
||||
{
|
||||
#define CHECK(x) if ((x)<0) return false;
|
||||
this->sock = parent;
|
||||
this->fd = parent->get_fd();
|
||||
|
||||
this->session = NULL;
|
||||
this->block = false;
|
||||
setblock(true);
|
||||
|
||||
CHECK(gnutls_init(&this->session, GNUTLS_CLIENT));
|
||||
if (!permissive)
|
||||
{
|
||||
gnutls_session_set_ptr(this->session, (void*)(const char*)domain);
|
||||
}
|
||||
gnutls_server_name_set(this->session, GNUTLS_NAME_DNS, domain, domain.length());
|
||||
|
||||
CHECK(gnutls_set_default_priority(this->session));
|
||||
//CHECK(gnutls_priority_set_direct(session, "NORMAL", NULL));
|
||||
|
||||
CHECK(gnutls_credentials_set(this->session, GNUTLS_CRD_CERTIFICATE, xcred));
|
||||
gnutls_transport_set_int(this->session, this->fd);
|
||||
gnutls_handshake_set_timeout(this->session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
||||
|
||||
int ret;
|
||||
do {
|
||||
ret = gnutls_handshake(session);
|
||||
}
|
||||
while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
|
||||
if (ret < 0) return false;
|
||||
|
||||
//char* desc = gnutls_session_get_desc(session);
|
||||
//puts(desc);
|
||||
//gnutls_free(desc);
|
||||
|
||||
return true;
|
||||
#undef CHECK
|
||||
}
|
||||
|
||||
/*private*/ int fixretrecv(int ret)
|
||||
{
|
||||
if (ret > 0) return ret;
|
||||
if (ret == 0) return e_closed;
|
||||
|
||||
if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) return 0;
|
||||
return e_ssl_failure;
|
||||
}
|
||||
|
||||
/*private*/ int fixretsend(int ret)
|
||||
{
|
||||
if (ret >= 0) return ret;
|
||||
else return fixretrecv(ret);
|
||||
}
|
||||
|
||||
void setblock(bool block)
|
||||
{
|
||||
if (block == this->block) return;
|
||||
this->block = block;
|
||||
socket::setblock(this->fd, block);
|
||||
}
|
||||
|
||||
int recv(arrayvieww<uint8_t> data, bool block = false)
|
||||
{
|
||||
setblock(block);
|
||||
return fixretrecv(gnutls_record_recv(this->session, data.ptr(), data.size()));
|
||||
}
|
||||
|
||||
int sendp(arrayview<uint8_t> data, bool block = true)
|
||||
{
|
||||
setblock(block);
|
||||
return fixretsend(gnutls_record_send(this->session, data.ptr(), data.size()));
|
||||
}
|
||||
|
||||
~socketssl_impl()
|
||||
{
|
||||
if (this->session)
|
||||
{
|
||||
gnutls_bye(this->session, GNUTLS_SHUT_RDWR); // can fail, but let's just ignore that
|
||||
}
|
||||
gnutls_deinit(this->session);
|
||||
delete sock;
|
||||
}
|
||||
};
|
||||
|
||||
socketssl* socketssl::create(socket* parent, cstring domain, bool permissive)
|
||||
{
|
||||
if (!parent) return NULL;
|
||||
|
||||
initialize();
|
||||
if (!init_ok) return NULL;
|
||||
|
||||
socketssl_impl* ret = new socketssl_impl;
|
||||
if (!ret->init(parent, domain, permissive))
|
||||
{
|
||||
delete ret;
|
||||
return NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
SSL* ssl;
|
||||
//bool nonblock;
|
||||
|
||||
static socketssl_impl* create(socket* parent, const char * domain, bool permissive)
|
||||
static socketssl_impl* create(socket* parent, cstring domain, bool permissive)
|
||||
{
|
||||
if (!parent) return NULL;
|
||||
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
ret->sock = parent;
|
||||
ret->fd = parent->get_fd();
|
||||
ret->ssl = SSL_new(ctx);
|
||||
SSL_set_tlsext_host_name(ret->ssl, (const char*)domain);
|
||||
//ret->nonblock = false;
|
||||
SSL_set_fd(ret->ssl, ret->fd);
|
||||
//TODO: set fd to nonblock
|
||||
@@ -62,7 +63,7 @@ public:
|
||||
bool ok = (SSL_connect(ret->ssl)==1);
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10002000 // < 1.0.2
|
||||
if (ok && !validate_hostname(domain, SSL_get_peer_certificate(ret->ssl)))
|
||||
if (ok && !permissive && !validate_hostname(domain, SSL_get_peer_certificate(ret->ssl)))
|
||||
{
|
||||
ok=false;
|
||||
}
|
||||
@@ -71,7 +72,7 @@ public:
|
||||
if (!ok)
|
||||
{
|
||||
delete ret;
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -88,14 +89,14 @@ public:
|
||||
}
|
||||
|
||||
//only supports nonblocking
|
||||
int recv(uint8_t* data, unsigned int len, bool block = false)
|
||||
int recv(arrayvieww<uint8_t> data, bool block = false)
|
||||
{
|
||||
return fixret(SSL_read(ssl, data, len));
|
||||
return fixret(SSL_read(ssl, data.ptr(), data.size()));
|
||||
}
|
||||
|
||||
int sendp(const uint8_t* data, unsigned int len, bool block = true)
|
||||
int sendp(arrayview<uint8_t> data, bool block = true)
|
||||
{
|
||||
return fixret(SSL_write(ssl, data, len));
|
||||
return fixret(SSL_write(ssl, data.ptr(), data.size()));
|
||||
}
|
||||
|
||||
~socketssl_impl()
|
||||
@@ -106,7 +107,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
socketssl* socketssl::create(socket* parent, const char * domain, bool permissive)
|
||||
socketssl* socketssl::create(socket* parent, cstring domain, bool permissive)
|
||||
{
|
||||
initialize();
|
||||
if (!ctx) return NULL;
|
||||
|
||||
@@ -54,7 +54,7 @@ static void initialize()
|
||||
SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
|
||||
SchannelCred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_USE_STRONG_CRYPTO;
|
||||
// fun fact: IE11 doesn't use SCH_USE_STRONG_CRYPTO. I guess it favors accepting outdated servers over rejecting evil ones.
|
||||
SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT; // Microsoft recommends setting this to zero, but that makes it use TLS 1.0, which sucks.
|
||||
SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT; // Microsoft recommends setting this to zero, but that makes it use TLS 1.0.
|
||||
//howsmyssl expects session ticket support for the Good rating, but that's only supported on windows 8, according to
|
||||
// https://connect.microsoft.com/IE/feedback/details/997136/internet-explorer-11-on-windows-7-does-not-support-tls-session-tickets
|
||||
//and I can't find which flag enables that, anyways
|
||||
@@ -75,23 +75,17 @@ public:
|
||||
size_t ret_buf_len;
|
||||
|
||||
bool in_handshake;
|
||||
bool permissive;
|
||||
|
||||
void fetch(bool block)
|
||||
{
|
||||
int bytes = sock->recv(recv_buf+recv_buf_len, 1024, block);
|
||||
if (bytes < 0)
|
||||
{
|
||||
delete sock;
|
||||
sock = NULL;
|
||||
}
|
||||
if (bytes > 0)
|
||||
{
|
||||
recv_buf_len += bytes;
|
||||
if (recv_buf_len > 1024)
|
||||
{
|
||||
recv_buf = realloc(recv_buf, recv_buf_len + 1024);
|
||||
}
|
||||
}
|
||||
array<byte> retbytes;
|
||||
int ret = sock->recv(retbytes, block);
|
||||
if (ret<0) return error();
|
||||
|
||||
recv_buf = realloc(recv_buf, recv_buf_len + ret + 1024);
|
||||
memcpy(recv_buf+recv_buf_len, retbytes.ptr(), ret);
|
||||
recv_buf_len += ret;
|
||||
}
|
||||
|
||||
void fetch() { fetch(true); }
|
||||
@@ -122,6 +116,7 @@ public:
|
||||
SSPI->DeleteSecurityContext(&ssl);
|
||||
delete sock;
|
||||
sock = NULL;
|
||||
in_handshake = false;
|
||||
}
|
||||
|
||||
void handshake()
|
||||
@@ -136,7 +131,9 @@ public:
|
||||
|
||||
DWORD ignore;
|
||||
SECURITY_STATUS scRet;
|
||||
scRet = SSPI->InitializeSecurityContextA(&cred, &ssl, NULL, SSPIFlags, 0, SECURITY_NATIVE_DREP,
|
||||
ULONG flags = SSPIFlags;
|
||||
if (this->permissive) flags |= ISC_REQ_MANUAL_CRED_VALIDATION; // +1 for defaulting to secure
|
||||
scRet = SSPI->InitializeSecurityContextA(&cred, &ssl, NULL, flags, 0, SECURITY_NATIVE_DREP,
|
||||
&InBufferDesc, 0, NULL, &OutBufferDesc, &ignore, NULL);
|
||||
|
||||
// according to the original program, extended errors are success
|
||||
@@ -146,7 +143,7 @@ public:
|
||||
{
|
||||
if (OutBuffer.cbBuffer != 0 && OutBuffer.pvBuffer != NULL)
|
||||
{
|
||||
if (sock->send((BYTE*)OutBuffer.pvBuffer, OutBuffer.cbBuffer) < 0)
|
||||
if (sock->send(arrayview<byte>((BYTE*)OutBuffer.pvBuffer, OutBuffer.cbBuffer)) < 0)
|
||||
{
|
||||
SSPI->FreeContextBuffer(OutBuffer.pvBuffer);
|
||||
error();
|
||||
@@ -195,7 +192,7 @@ public:
|
||||
|
||||
if (OutBuffer.cbBuffer != 0)
|
||||
{
|
||||
if (sock->send((BYTE*)OutBuffer.pvBuffer, OutBuffer.cbBuffer) < 0)
|
||||
if (sock->send(arrayview<byte>((BYTE*)OutBuffer.pvBuffer, OutBuffer.cbBuffer)) < 0)
|
||||
{
|
||||
SSPI->FreeContextBuffer(OutBuffer.pvBuffer);
|
||||
error();
|
||||
@@ -213,12 +210,14 @@ public:
|
||||
{
|
||||
if (!parent) return false;
|
||||
|
||||
sock = parent;
|
||||
fd = parent->get_fd();
|
||||
recv_buf = malloc(2048);
|
||||
recv_buf_len = 0;
|
||||
ret_buf = malloc(2048);
|
||||
ret_buf_len = 0;
|
||||
this->sock = parent;
|
||||
this->fd = parent->get_fd();
|
||||
this->recv_buf = malloc(2048);
|
||||
this->recv_buf_len = 0;
|
||||
this->ret_buf = malloc(2048);
|
||||
this->ret_buf_len = 0;
|
||||
|
||||
this->permissive = permissive;
|
||||
|
||||
if (!handshake_first(domain)) return false;
|
||||
SSPI->QueryContextAttributes(&ssl, SECPKG_ATTR_STREAM_SIZES, &bufsizes);
|
||||
@@ -276,26 +275,28 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
int recv(uint8_t* data, unsigned int len, bool block = false)
|
||||
int recv(arrayvieww<byte> data, bool block = false)
|
||||
{
|
||||
if (!sock) return -1;
|
||||
|
||||
fetch(block);
|
||||
process();
|
||||
|
||||
if (!ret_buf_len) return 0;
|
||||
|
||||
unsigned ulen = len;
|
||||
int ret = (ulen < ret_buf_len ? ulen : ret_buf_len);
|
||||
memcpy(data, ret_buf, ret);
|
||||
memmove(ret_buf, ret_buf+ret, ret_buf_len-ret);
|
||||
ret_buf_len -= ret;
|
||||
return ret;
|
||||
size_t bytes_ret = ret_buf_len;
|
||||
if (bytes_ret > data.size()) bytes_ret = data.size();
|
||||
memcpy(data.ptr(), ret_buf, bytes_ret);
|
||||
memmove(ret_buf, ret_buf+bytes_ret, ret_buf_len-bytes_ret);
|
||||
ret_buf_len -= bytes_ret;
|
||||
return bytes_ret;
|
||||
}
|
||||
|
||||
int sendp(const uint8_t* data, unsigned int len, bool block = true)
|
||||
int sendp(arrayview<byte> bytes, bool block = true)
|
||||
{
|
||||
if (!sock) return -1;
|
||||
|
||||
const byte* data = bytes.ptr();
|
||||
unsigned int len = bytes.size();
|
||||
|
||||
fetchnb();
|
||||
process();
|
||||
|
||||
@@ -314,7 +315,7 @@ public:
|
||||
SecBufferDesc Message = { SECBUFFER_VERSION, 4, Buffers };
|
||||
if (FAILED(SSPI->EncryptMessage(&ssl, 0, &Message, 0))) { error(); return -1; }
|
||||
|
||||
if (sock->send(sendbuf, Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer) < 0) error();
|
||||
if (sock->send(arrayview<byte>(sendbuf, Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer)) < 0) error();
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -329,7 +330,7 @@ public:
|
||||
|
||||
}
|
||||
|
||||
socketssl* socketssl::create(socket* parent, const char * domain, bool permissive)
|
||||
socketssl* socketssl::create(socket* parent, cstring domain, bool permissive)
|
||||
{
|
||||
initialize();
|
||||
socketssl_impl* ret = new socketssl_impl();
|
||||
|
||||
@@ -1,5 +1,97 @@
|
||||
#include "../arlib.h"
|
||||
#include "../test.h"
|
||||
|
||||
//TODO:
|
||||
//- fetch howsmyssl, ensure the only failure is the session cache
|
||||
//- ensure Subject Name is verified: fetch https://172.217.18.142/ (IP of google.com)
|
||||
//- ensure bad roots are rejected: fetch https://badfish.filippo.io/
|
||||
//- ensure bad certs are accepted with verification off
|
||||
|
||||
#ifdef ARLIB_TEST
|
||||
//not in socket.h because this shouldn't really be used for anything, blocking is evil
|
||||
static array<byte> recvall(socket* sock, unsigned int len)
|
||||
{
|
||||
array<byte> ret;
|
||||
ret.resize(len);
|
||||
|
||||
size_t pos = 0;
|
||||
while (pos < len)
|
||||
{
|
||||
int part = sock->recv(ret.slice(pos, (pos==0)?2:1), true); // funny slicing to ensure partial reads are processed sensibly
|
||||
assert_ret(part >= 0, NULL);
|
||||
assert_ret(part > 0, NULL); // this is a blocking recv, returning zero is forbidden
|
||||
pos += part;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void clienttest(socket* rs)
|
||||
{
|
||||
//returns whether the socket peer speaks HTTP
|
||||
//discards the actual response, and since the Host: header is silly, it's most likely some variant of 404 not found
|
||||
//also closes the socket
|
||||
|
||||
autoptr<socket> s = rs;
|
||||
assert(s);
|
||||
|
||||
//in HTTP, client talks first, ensure this doesn't return anything
|
||||
array<byte> discard;
|
||||
discard.resize(1);
|
||||
assert(s->recv(discard) == 0);
|
||||
|
||||
const char http_get[] =
|
||||
"GET / HTTP/1.1\n"
|
||||
"Host: example.com\n"
|
||||
"Connection: close\n"
|
||||
"\n";
|
||||
assert_eq(s->send(http_get), (int)strlen(http_get));
|
||||
|
||||
array<byte> ret = recvall(s, 4);
|
||||
assert(ret.size() == 4);
|
||||
assert(!memcmp(ret.ptr(), "HTTP", 4));
|
||||
}
|
||||
|
||||
test("plaintext client") { clienttest(socket::create("google.com", 80)); }
|
||||
test("SSL client") { clienttest(socketssl::create("google.com", 443)); }
|
||||
test("SSL SNI") { clienttest(socketssl::create("git.io", 443)); } // this server throws an error unless SNI is enabled
|
||||
test("SSL permissiveness")
|
||||
{
|
||||
autoptr<socket> s;
|
||||
assert(!(s=socketssl::create("badfish.filippo.io", 443))); // invalid cert root
|
||||
assert( (s=socketssl::create("badfish.filippo.io", 443, true)));
|
||||
assert(!(s=socketssl::create("172.217.18.142", 443))); // invalid subject name, IP addresses don't have certs (this is Google)
|
||||
assert( (s=socketssl::create("172.217.18.142", 443, true))); // I'd use san.filippo.io, but that one is self-signed as well; I want only one failure at once
|
||||
}
|
||||
|
||||
void listentest(const char * localhost, int port)
|
||||
{
|
||||
autoptr<socketlisten> l = socketlisten::create(port);
|
||||
assert(l);
|
||||
autoptr<socket> c1 = socket::create(localhost, port);
|
||||
assert(c1);
|
||||
|
||||
#ifdef _WIN32
|
||||
//apparently the connection takes a while to make it through the kernel, at least on windows
|
||||
//socket* lr = l; // can't select &l because autoptr<socketlisten>* isn't socket**
|
||||
//assert(socket::select(&lr, 1, 100) == 0); // TODO: enable select()
|
||||
Sleep(50);
|
||||
#endif
|
||||
autoptr<socket> c2 = l->accept();
|
||||
assert(c2);
|
||||
|
||||
l = NULL;
|
||||
|
||||
c1->send("foo");
|
||||
c2->send("bar");
|
||||
|
||||
array<byte> ret;
|
||||
ret = recvall(c1, 3);
|
||||
assert(ret.size() == 3);
|
||||
assert(!memcmp(ret.ptr(), "bar", 3));
|
||||
|
||||
ret = recvall(c2, 3);
|
||||
assert(ret.size() == 3);
|
||||
assert(!memcmp(ret.ptr(), "foo", 3));
|
||||
}
|
||||
|
||||
test("listen on localhost") { listentest("localhost", 7777); }
|
||||
test("listen on 127.0.0.1") { listentest("127.0.0.1", 7778); }
|
||||
test("listen on ::1") { listentest("::1", 7779); }
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#ifdef ARLIB_SSL_TLSE
|
||||
extern "C" {
|
||||
#include "tlse.h"
|
||||
#include "../deps/tlse.h"
|
||||
}
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
@@ -123,12 +123,12 @@ public:
|
||||
const uint8_t * out = tls_get_write_buffer(ssl, &outlen);
|
||||
if (out && outlen)
|
||||
{
|
||||
if (sock->send(out, outlen) < 0) { error(); return; }
|
||||
if (sock->send(arrayview<byte>(out, outlen)) < 0) { error(); return; }
|
||||
tls_buffer_clear(ssl);
|
||||
}
|
||||
|
||||
uint8_t in[0x2000];
|
||||
int inlen = sock->recv(in, sizeof(in), block);
|
||||
int inlen = sock->recv(arrayvieww<byte>(in, sizeof(in)), block);
|
||||
if (inlen<0) { error(); return; }
|
||||
if (inlen>0) tls_consume_stream(ssl, in, inlen, verify);
|
||||
}
|
||||
@@ -162,21 +162,21 @@ public:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int recv(uint8_t* data, unsigned int len, bool block = false)
|
||||
int recv(arrayvieww<byte> data, bool block = false)
|
||||
{
|
||||
process(block);
|
||||
|
||||
int ret = tls_read(ssl, data, len);
|
||||
int ret = tls_read(ssl, data.ptr(), data.size());
|
||||
if (ret==0 && !sock) return e_broken;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sendp(const uint8_t* data, unsigned int len, bool block = true)
|
||||
int sendp(arrayview<byte> data, bool block = true)
|
||||
{
|
||||
if (!sock) return -1;
|
||||
|
||||
int ret = tls_write(ssl, (uint8_t*)data, len);
|
||||
int ret = tls_write(ssl, (uint8_t*)data.ptr(), data.size());
|
||||
process(false);
|
||||
return ret;
|
||||
}
|
||||
@@ -192,69 +192,69 @@ public:
|
||||
if (sock) delete sock;
|
||||
}
|
||||
|
||||
void q()
|
||||
{
|
||||
uint8_t data[4096];
|
||||
int len = tls_export_context(ssl, NULL, 0, false);
|
||||
int len2 = tls_export_context(ssl, data, len, false);
|
||||
printf("len=%i len2=%i\n", len, len2);
|
||||
//tls_destroy_context(ssl);
|
||||
|
||||
TLSContext* ssl2 = tls_import_context(data, len);
|
||||
|
||||
uint8_t* p1 = (uint8_t*)ssl;
|
||||
uint8_t* p2 = (uint8_t*)ssl2;
|
||||
for (int i=0;i<140304;i++)
|
||||
{
|
||||
//if (p1[i] != p2[i]) printf("%i: g=%.2X b=%.2X\n", i, p1[i], p2[i]);
|
||||
}
|
||||
|
||||
//ssl = ssl2;
|
||||
}
|
||||
//void q()
|
||||
//{
|
||||
// uint8_t data[4096];
|
||||
// int len = tls_export_context(ssl, NULL, 0, false);
|
||||
// int len2 = tls_export_context(ssl, data, len, false);
|
||||
// printf("len=%i len2=%i\n", len, len2);
|
||||
// //tls_destroy_context(ssl);
|
||||
//
|
||||
// TLSContext* ssl2 = tls_import_context(data, len);
|
||||
//
|
||||
// uint8_t* p1 = (uint8_t*)ssl;
|
||||
// uint8_t* p2 = (uint8_t*)ssl2;
|
||||
// for (int i=0;i<140304;i++)
|
||||
// {
|
||||
// //if (p1[i] != p2[i]) printf("%i: g=%.2X b=%.2X\n", i, p1[i], p2[i]);
|
||||
// }
|
||||
//
|
||||
// //ssl = ssl2;
|
||||
//}
|
||||
|
||||
|
||||
size_t serialize_size()
|
||||
{
|
||||
return tls_export_context(ssl, NULL, 0, false);
|
||||
}
|
||||
|
||||
int serialize(uint8_t* data, size_t len)
|
||||
{
|
||||
process(true);
|
||||
|
||||
tls_export_context(ssl, data, len, false);
|
||||
|
||||
tls_destroy_context(this->ssl);
|
||||
this->ssl = NULL;
|
||||
|
||||
int ret = decompose(this->sock);
|
||||
this->sock = NULL;
|
||||
|
||||
delete this;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static socketssl_impl* unserialize(int fd, const uint8_t* data, size_t len)
|
||||
{
|
||||
socketssl_impl* ret = new socketssl_impl();
|
||||
ret->sock = socket::create_from_fd(fd);
|
||||
ret->fd = fd;
|
||||
ret->ssl = tls_import_context((uint8_t*)data, len);
|
||||
if (!ret->ssl) { delete ret; return NULL; }
|
||||
return ret;
|
||||
}
|
||||
//size_t serialize_size()
|
||||
//{
|
||||
// return tls_export_context(ssl, NULL, 0, false);
|
||||
//}
|
||||
//
|
||||
//int serialize(uint8_t* data, size_t len)
|
||||
//{
|
||||
// process(true);
|
||||
//
|
||||
// tls_export_context(ssl, data, len, false);
|
||||
//
|
||||
// tls_destroy_context(this->ssl);
|
||||
// this->ssl = NULL;
|
||||
//
|
||||
// int ret = decompose(this->sock);
|
||||
// this->sock = NULL;
|
||||
//
|
||||
// delete this;
|
||||
//
|
||||
// return ret;
|
||||
//}
|
||||
//
|
||||
//static socketssl_impl* unserialize(int fd, const uint8_t* data, size_t len)
|
||||
//{
|
||||
// socketssl_impl* ret = new socketssl_impl();
|
||||
// ret->sock = socket::create_from_fd(fd);
|
||||
// ret->fd = fd;
|
||||
// ret->ssl = tls_import_context((uint8_t*)data, len);
|
||||
// if (!ret->ssl) { delete ret; return NULL; }
|
||||
// return ret;
|
||||
//}
|
||||
};
|
||||
|
||||
socketssl* socketssl::create(socket* parent, const char * domain, bool permissive)
|
||||
socketssl* socketssl::create(socket* parent, cstring domain, bool permissive)
|
||||
{
|
||||
initialize();
|
||||
return socketssl_impl::create(parent, domain, permissive);
|
||||
}
|
||||
|
||||
socketssl* socketssl::unserialize(int fd, const uint8_t* data, size_t len)
|
||||
{
|
||||
initialize();
|
||||
return socketssl_impl::unserialize(fd, data, len);
|
||||
}
|
||||
//socketssl* socketssl::unserialize(int fd, const uint8_t* data, size_t len)
|
||||
//{
|
||||
// initialize();
|
||||
// return socketssl_impl::unserialize(fd, data, len);
|
||||
//}
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <mstcpip.h>
|
||||
#define MSG_NOSIGNAL 0
|
||||
#define MSG_DONTWAIT 0
|
||||
#define close closesocket
|
||||
@@ -20,13 +21,12 @@
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
static int setsockopt(int socket, int level, int option_name, int option_value)
|
||||
{
|
||||
return setsockopt(socket, level, option_name, &option_value, sizeof(option_value));
|
||||
}
|
||||
#endif
|
||||
|
||||
//wrapper because 'socket' is a type in this code, so socket(2) needs another name
|
||||
static int mksocket(int domain, int type, int protocol) { return socket(domain, type, protocol); }
|
||||
#define socket socket_t
|
||||
|
||||
namespace {
|
||||
|
||||
static void initialize()
|
||||
@@ -41,6 +41,16 @@ static void initialize()
|
||||
#endif
|
||||
}
|
||||
|
||||
static int setsockopt(int socket, int level, int option_name, const void * option_value, socklen_t option_len)
|
||||
{
|
||||
return ::setsockopt(socket, level, option_name, (char*)/*lol windows*/option_value, option_len);
|
||||
}
|
||||
|
||||
static int setsockopt(int socket, int level, int option_name, int option_value)
|
||||
{
|
||||
return setsockopt(socket, level, option_name, &option_value, sizeof(option_value));
|
||||
}
|
||||
|
||||
static int connect(const char * domain, int port)
|
||||
{
|
||||
initialize();
|
||||
@@ -58,7 +68,7 @@ static int connect(const char * domain, int port)
|
||||
getaddrinfo(domain, portstr, &hints, &addr);
|
||||
if (!addr) return -1;
|
||||
|
||||
int fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
||||
int fd = mksocket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
||||
#ifndef _WIN32
|
||||
//because 30 second pauses are unequivocally detestable
|
||||
timeval timeout;
|
||||
@@ -97,9 +107,38 @@ static int connect(const char * domain, int port)
|
||||
return fd;
|
||||
}
|
||||
|
||||
#define socket socket_t
|
||||
} // close namespace
|
||||
|
||||
//MSG_DONTWAIT is usually better, but accept() doesn't take that argument
|
||||
void socket::setblock(int fd, bool newblock)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
u_long nonblock = !newblock;
|
||||
ioctlsocket(fd, FIONBIO, &nonblock);
|
||||
#else
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
flags &= ~O_NONBLOCK;
|
||||
if (!newblock) flags |= O_NONBLOCK;
|
||||
fcntl(fd, F_SETFL, flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class socket_impl : public socket {
|
||||
public:
|
||||
#ifdef _WIN32
|
||||
bool m_blocking = true;
|
||||
/*private*/ void setblock(bool newblock)
|
||||
{
|
||||
//if (m_blocking == newblock) return;
|
||||
//m_blocking = newblock;
|
||||
socket::setblock(this->fd, newblock);
|
||||
}
|
||||
#else
|
||||
/*private*/ void setblock(bool newblock) {} // MSG_DONTWAIT exists here
|
||||
#endif
|
||||
|
||||
socket_impl(int fd) { this->fd = fd; }
|
||||
|
||||
/*private*/ int fixret(int ret)
|
||||
@@ -116,15 +155,16 @@ public:
|
||||
return e_broken;
|
||||
}
|
||||
|
||||
int recv(uint8_t* data, unsigned int len, bool block = false)
|
||||
int recv(arrayvieww<byte> data, bool block = false)
|
||||
{
|
||||
return fixret(::recv(fd, (char*)data, len, MSG_NOSIGNAL | (block ? 0 : MSG_DONTWAIT)));
|
||||
setblock(block);
|
||||
return fixret(::recv(this->fd, (char*)data.ptr(), data.size(), MSG_NOSIGNAL | (block ? 0 : MSG_DONTWAIT)));
|
||||
}
|
||||
|
||||
int sendp(const uint8_t* data, unsigned int len, bool block = true)
|
||||
int sendp(arrayview<byte> data, bool block = true)
|
||||
{
|
||||
//printf("snd=%i\n",len);
|
||||
return fixret(::send(fd, (char*)data, len, MSG_NOSIGNAL | (block ? 0 : MSG_DONTWAIT)));
|
||||
setblock(block);
|
||||
return fixret(::send(fd, (char*)data.ptr(), data.size(), MSG_NOSIGNAL | (block ? 0 : MSG_DONTWAIT)));
|
||||
}
|
||||
|
||||
~socket_impl()
|
||||
@@ -146,15 +186,78 @@ socket* socket::create_from_fd(int fd)
|
||||
return socket_wrap(fd);
|
||||
}
|
||||
|
||||
socket* socket::create(const char * domain, int port)
|
||||
socket* socket::create(cstring domain, int port)
|
||||
{
|
||||
return socket_wrap(connect(domain, port));
|
||||
}
|
||||
|
||||
//static socket* create_async(const char * domain, int port);
|
||||
//static socket* create_udp(const char * domain, int port);
|
||||
//static socket* socket::create_async(const char * domain, int port);
|
||||
//static socket* socket::create_udp(const char * domain, int port);
|
||||
|
||||
//int socket::select(socket* * socks, int nsocks, int timeout_ms)
|
||||
//{
|
||||
// return -1;
|
||||
//}
|
||||
|
||||
|
||||
static MAYBE_UNUSED int socketlisten_create_ip4(int port)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
sa.sin_port = htons(port);
|
||||
|
||||
int fd = mksocket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd < 0) goto fail;
|
||||
|
||||
if (bind(fd, (struct sockaddr*)&sa, sizeof(sa)) < 0) goto fail;
|
||||
if (listen(fd, 10) < 0) goto fail;
|
||||
return fd;
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int socketlisten_create_ip6(int port)
|
||||
{
|
||||
struct sockaddr_in6 sa; // IN6ADDR_ANY_INIT should work, but doesn't.
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sin6_family = AF_INET6;
|
||||
sa.sin6_addr = in6addr_any;
|
||||
sa.sin6_port = htons(port);
|
||||
|
||||
int fd = mksocket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (fd < 0) goto fail;
|
||||
|
||||
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, false) < 0) goto fail;
|
||||
if (bind(fd, (struct sockaddr*)&sa, sizeof(sa)) < 0) goto fail;
|
||||
if (listen(fd, 10) < 0) goto fail;
|
||||
return fd;
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
socketlisten* socketlisten::create(int port)
|
||||
{
|
||||
int fd = -1;
|
||||
if (fd<0) fd = socketlisten_create_ip6(port);
|
||||
#if defined(_WIN32)
|
||||
//Windows XP can't dualstack the v6 addresses, so let's keep the fallback
|
||||
if (fd<0) fd = socketlisten_create_ip4(port);
|
||||
#endif
|
||||
if (fd<0) return NULL;
|
||||
|
||||
setblock(fd, false);
|
||||
return new socketlisten(fd);
|
||||
}
|
||||
|
||||
socket* socketlisten::accept()
|
||||
{
|
||||
return socket_wrap(::accept(this->fd, NULL,NULL));
|
||||
}
|
||||
|
||||
socketlisten::~socketlisten() { close(this->fd); }
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#include "../global.h"
|
||||
#include <stdbool.h>
|
||||
#include "../containers.h"
|
||||
#include "../function.h"
|
||||
#include "../string.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
//TODO: fiddle with https://github.com/ckennelly/hole-punch
|
||||
|
||||
#define socket socket_t
|
||||
class socket : nocopy {
|
||||
protected:
|
||||
@@ -12,12 +16,15 @@ protected:
|
||||
//deallocates the socket, returning its fd, while letting the fd remain valid
|
||||
static int decompose(socket* sock) { int ret = sock->fd; sock->fd=-1; delete sock; return ret; }
|
||||
|
||||
static void setblock(int fd, bool newblock);
|
||||
|
||||
public:
|
||||
//Returns NULL on connection failure.
|
||||
static socket* create(const char * domain, int port);
|
||||
static socket* create(cstring domain, int port);
|
||||
//Always succeeds. If the server can't be contacted, returns failure on first write or read.
|
||||
static socket* create_async(const char * domain, int port);
|
||||
static socket* create_udp(const char * domain, int port);
|
||||
static socket* create_async(cstring domain, int port);
|
||||
//Always succeeds. If the server can't be contacted, may return e_broken at some point, or may just discard everything.
|
||||
static socket* create_udp(cstring domain, int port);
|
||||
|
||||
enum {
|
||||
e_lazy_dev = -1, // Whoever implemented this socket layer was lazy and just returned -1. Treat it as e_broken or an unknown error.
|
||||
@@ -25,23 +32,28 @@ public:
|
||||
e_broken = -3, // Connection was forcibly torn down.
|
||||
e_udp_too_big = -4, // Attempted to process an unacceptably large UDP packet.
|
||||
e_ssl_failure = -5, // Certificate validation failed, no algorithms in common, or other SSL error.
|
||||
e_not_supported = -6, // Attempted to read or write a listening socket, or other unsupported operation.
|
||||
};
|
||||
|
||||
//Negative means error, see above.
|
||||
//Positive is number of bytes handled.
|
||||
//WARNING: Unlike most socket layers, zero does not mean graceful close!
|
||||
// It means success, zero bytes processed, and is a valid byte count. Socket closed is in the error list above.
|
||||
//WARNING: Most socket APIs treat read/write of zero bytes as EOF. Not this one! 0 is EWOULDBLOCK; EOF is an error.
|
||||
//The first two functions will process at least one byte, or if block is false, at least zero. send() sends all bytes before returning.
|
||||
//block is ignored on Windows (always false), due to lack of MSG_NOWAIT and I don't want to do another syscall every time.
|
||||
//For UDP sockets, partial reads or writes aren't possible; you always get one or zero packets.
|
||||
virtual int recv(uint8_t* data, unsigned int len, bool block = true) = 0;
|
||||
virtual int sendp(const uint8_t* data, unsigned int len, bool block = true) = 0;
|
||||
int send(const uint8_t* data, unsigned int len)
|
||||
virtual int recv(arrayvieww<byte> data, bool block = false) = 0;
|
||||
int recv(array<byte>& data, bool block = false)
|
||||
{
|
||||
if (data.size()==0) data.resize(4096);
|
||||
return recv((arrayvieww<byte>)data, block);
|
||||
}
|
||||
virtual int sendp(arrayview<byte> data, bool block = true) = 0;
|
||||
|
||||
int send(arrayview<byte> bytes)
|
||||
{
|
||||
const byte * data = bytes.ptr();
|
||||
unsigned int len = bytes.size();
|
||||
unsigned int sent = 0;
|
||||
while (sent < len)
|
||||
{
|
||||
int here = sendp(data+sent, len-sent);
|
||||
int here = this->sendp(arrayview<byte>(data+sent, len-sent));
|
||||
if (here<0) return here;
|
||||
sent += here;
|
||||
}
|
||||
@@ -49,22 +61,19 @@ public:
|
||||
}
|
||||
|
||||
//Convenience functions for handling textual data.
|
||||
int recv(char* data, unsigned int len, bool block = false)
|
||||
{
|
||||
int ret = recv((uint8_t*)data, len-1, block);
|
||||
if (ret >= 0) data[ret]='\0';
|
||||
else data[0]='\0';
|
||||
return ret;
|
||||
}
|
||||
int sendp(const char * data, bool block = true) { return sendp((uint8_t*)data, strlen(data), block); }
|
||||
int send (const char * data) { return send((uint8_t*)data, strlen(data)); }
|
||||
//maybe<string> recvstr(bool block = false)
|
||||
//{
|
||||
// maybe<array<byte>> ret = this->recv(block);
|
||||
// if (!ret) return maybe<string>(NULL, ret.error);
|
||||
// return maybe<string>((string)ret.value);
|
||||
//}
|
||||
int sendp(cstring data, bool block = true) { return this->sendp(data.bytes(), block); }
|
||||
int send(cstring data) { return this->send(data.bytes()); }
|
||||
|
||||
//Returns an index to the sockets array, or negative if timeout expires.
|
||||
//Negative timeouts mean wait forever.
|
||||
//It's possible that an active socket returns zero bytes.
|
||||
//However, this is guaranteed to happen rarely enough that repeatedly select()ing will leave the CPU mostly idle.
|
||||
//Returns an index to the sockets array, or negative if timeout expires. Negative timeout mean wait forever.
|
||||
//It's possible that an active socket returns zero bytes. However, this is rare; repeatedly select()ing and processing the data will eventually sleep.
|
||||
//(It may be caused by packets with wrong checksum, SSL renegotiation, or whatever.)
|
||||
static int select(socket* * socks, unsigned int nsocks, int timeout_ms = -1);
|
||||
//static int select(socket* * socks, unsigned int nsocks, int timeout_ms = -1);
|
||||
|
||||
virtual ~socket() {}
|
||||
|
||||
@@ -77,23 +86,38 @@ class socketssl : public socket {
|
||||
protected:
|
||||
socketssl(){}
|
||||
public:
|
||||
//If 'permissive' is true, expired and self-signed server certificates will be accepted.
|
||||
//Other invalid certs, such as ones for a different domain, may or may not be accepted.
|
||||
static socketssl* create(const char * domain, int port, bool permissive=false)
|
||||
//If 'permissive' is true, the server certficate will be ignored.
|
||||
//Expired, self-signed, untrusted root, wrong domain, everything's fine.
|
||||
static socketssl* create(cstring domain, int port, bool permissive=false)
|
||||
{
|
||||
return socketssl::create(socket::create(domain, port), domain, permissive);
|
||||
}
|
||||
//On entry, this takes ownership of the socket. Even if connection fails, the socket may not be used anymore.
|
||||
//The socket must be a normal TCP socket. UDP and nested SSL is not supported.
|
||||
static socketssl* create(socket* parent, const char * domain, bool permissive=false);
|
||||
//The socket must be a normal TCP socket (create_async is fine). UDP and nested SSL is not supported.
|
||||
static socketssl* create(socket* parent, cstring domain, bool permissive=false);
|
||||
|
||||
|
||||
virtual void q(){}
|
||||
//set_cert or set_cert_cb must be called before read or write.
|
||||
static socketssl* create_server(socket* parent);
|
||||
//Only usable on server sockets.
|
||||
void set_cert(array<byte> data); // Must be called exactly once.
|
||||
void set_cert_cb(function<void(socketssl* sock, cstring hostname)> cb); // Used for SNI. The callback must call set_cert.
|
||||
|
||||
//Can be used to keep a socket alive across exec().
|
||||
//If successful, serialize() returns the the file descriptor needed to unserialize, and the socket is deleted.
|
||||
//If failure, negative return and nothing happens.
|
||||
//On failure, negative return and nothing happens.
|
||||
virtual size_t serialize_size() { return 0; }
|
||||
virtual int serialize(uint8_t* data, size_t len) { return -1; }
|
||||
static socketssl* unserialize(int fd, const uint8_t* data, size_t len);
|
||||
virtual tuple<int, array<byte>> serialize() { return tuple<int, array<byte>>(-1, NULL); }
|
||||
static socketssl* unserialize(tuple<int, array<byte>> data);
|
||||
};
|
||||
|
||||
//socket::select() works on these, but recv/send will fail
|
||||
class socketlisten : public socket {
|
||||
socketlisten(int fd) { this->fd = fd; }
|
||||
public:
|
||||
static socketlisten* create(int port);
|
||||
socket* accept();
|
||||
~socketlisten();
|
||||
|
||||
int recv(arrayvieww<byte> data, bool block) { return e_not_supported; }
|
||||
int sendp(arrayview<byte> data, bool block) { return e_not_supported; }
|
||||
};
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#define socklen_t int
|
||||
#define sleep(x) Sleep(x*1000)
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#include "tlse.c"
|
||||
|
||||
void error(char *msg) {
|
||||
perror(msg);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int send_pending(int client_sock, struct TLSContext *context) {
|
||||
unsigned int out_buffer_len = 0;
|
||||
const unsigned char *out_buffer = tls_get_write_buffer(context, &out_buffer_len);
|
||||
unsigned int out_buffer_index = 0;
|
||||
int send_res = 0;
|
||||
while ((out_buffer) && (out_buffer_len > 0)) {
|
||||
int res = send(client_sock, (char *)&out_buffer[out_buffer_index], out_buffer_len, 0);
|
||||
if (res <= 0) {
|
||||
send_res = res;
|
||||
break;
|
||||
}
|
||||
out_buffer_len -= res;
|
||||
out_buffer_index += res;
|
||||
}
|
||||
tls_buffer_clear(context);
|
||||
return send_res;
|
||||
}
|
||||
|
||||
int validate_certificate(struct TLSContext *context, struct TLSCertificate **certificate_chain, int len) {
|
||||
int i;
|
||||
if (certificate_chain) {
|
||||
for (i = 0; i < len; i++) {
|
||||
struct TLSCertificate *certificate = certificate_chain[i];
|
||||
// check certificate ...
|
||||
}
|
||||
}
|
||||
//return certificate_expired;
|
||||
//return certificate_revoked;
|
||||
//return certificate_unknown;
|
||||
return no_error;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int sockfd, portno, n;
|
||||
//tls_print_certificate("testcert/server.certificate");
|
||||
//tls_print_certificate("000.certificate");
|
||||
//exit(0);
|
||||
struct sockaddr_in serv_addr;
|
||||
struct hostent *server;
|
||||
|
||||
char buffer[256];
|
||||
char *ref_argv[] = {"", "google.com", "443"};
|
||||
if (argc < 3) {
|
||||
argv = ref_argv;
|
||||
//fprintf(stderr,"usage %s hostname port\n", argv[0]);
|
||||
//exit(0);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
#else
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
portno = atoi(argv[2]);
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0)
|
||||
error("ERROR opening socket");
|
||||
server = gethostbyname(argv[1]);
|
||||
if (server == NULL) {
|
||||
fprintf(stderr,"ERROR, no such host\n");
|
||||
exit(0);
|
||||
}
|
||||
memset((char *) &serv_addr, 0, sizeof(serv_addr));
|
||||
serv_addr.sin_family = AF_INET;
|
||||
memcpy((char *)&serv_addr.sin_addr.s_addr, (char *)server->h_addr, server->h_length);
|
||||
serv_addr.sin_port = htons(portno);
|
||||
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
|
||||
error("ERROR connecting");
|
||||
|
||||
struct TLSContext *context = tls_create_context(0, TLS_V12);
|
||||
tls_client_connect(context);
|
||||
send_pending(sockfd, context);
|
||||
unsigned char client_message[0xFFFF];
|
||||
int read_size;
|
||||
while ((read_size = recv(sockfd, client_message, sizeof(client_message) , 0)) > 0) {
|
||||
tls_consume_stream(context, client_message, read_size, validate_certificate);
|
||||
send_pending(sockfd, context);
|
||||
if (tls_established(context)) {
|
||||
const char * out = "GET / HTTP/1.1\nHost: example.com\nConnection: close\n\n";
|
||||
tls_write(context, out, strlen(out));
|
||||
send_pending(sockfd, context);
|
||||
|
||||
unsigned char read_buffer[0xFFFF];
|
||||
int read_size = tls_read(context, read_buffer, 0xFFFF - 1);
|
||||
if (read_size > 0)
|
||||
fwrite(read_buffer, read_size, 1, stdout);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
1555
arlib/socket/uuu.cpp
1555
arlib/socket/uuu.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,141 +0,0 @@
|
||||
#ifdef ARLIB_SSL_WOLFSSL_SP
|
||||
//I'll have to #include the parts of WolfSSL I need
|
||||
//it's easier in preprocessor than in makefile
|
||||
|
||||
#define DEBUG_WOLFSSL
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
//#define NO_WOLFSSL_MEMORY // use malloc like a sane program
|
||||
//#define NO_WOLFSSL_DIR // we scan the directories ourelves
|
||||
//#define WOLFSSL_USER_IO // we set our own read/write callbacks
|
||||
|
||||
#ifdef _WIN32
|
||||
#define USE_WINDOWS_API
|
||||
#else
|
||||
#define WOLFSSL_PTHREADS
|
||||
#endif
|
||||
|
||||
//#ifndef ARLIB_THREAD
|
||||
//#define SINGLE_THREADED
|
||||
//#endif
|
||||
|
||||
//got these from ./configure
|
||||
#define HAVE_THREAD_LS
|
||||
#define HAVE_AESGCM
|
||||
#define WOLFSSL_SHA512
|
||||
#define WOLFSSL_SHA384
|
||||
#define NO_DSA
|
||||
#define HAVE_ECC
|
||||
#define TFM_ECC256
|
||||
#define ECC_SHAMIR
|
||||
#define NO_RC4
|
||||
#define NO_HC128
|
||||
#define NO_RABBIT
|
||||
#define HAVE_POLY1305
|
||||
#define HAVE_ONE_TIME_AUTH
|
||||
#define HAVE_CHACHA
|
||||
#define HAVE_HASHDRBG
|
||||
#define HAVE_TLS_EXTENSIONS
|
||||
#define HAVE_SUPPORTED_CURVES
|
||||
#define NO_PSK
|
||||
#define NO_MD4
|
||||
#define NO_PWDBASED
|
||||
#define USE_FAST_MATH
|
||||
#define WOLFSSL_X86_64_BUILD
|
||||
#define HAVE___UINT128_T
|
||||
|
||||
#include "wolfssl-3.9.0/src/crl.c"
|
||||
#include "wolfssl-3.9.0/src/internal.c"
|
||||
#define c16toa c16toa_b // these functions are copypasted. should be in a header
|
||||
#define c32toa c32toa_b
|
||||
#define ato16 ato16_b
|
||||
#define c24to32 c24to32_b
|
||||
#define GetSEQIncrement GetSEQIncrement_b
|
||||
#include "wolfssl-3.9.0/src/io.c"
|
||||
#include "wolfssl-3.9.0/src/keys.c"
|
||||
#include "wolfssl-3.9.0/src/ocsp.c"
|
||||
#include "wolfssl-3.9.0/src/sniffer.c"
|
||||
#include "wolfssl-3.9.0/src/ssl.c"
|
||||
#include "wolfssl-3.9.0/src/tls.c"
|
||||
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/aes.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/arc4.c"
|
||||
//#include "wolfssl-3.9.0/wolfcrypt/src/asm.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/asn.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/blake2b.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/camellia.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/chacha20_poly1305.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/chacha.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/coding.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/compress.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/curve25519.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/des3.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/dh.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/dsa.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/ecc.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/ecc_fp.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/ed25519.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/error.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/fe_low_mem.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/fe_operations.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/ge_low_mem.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/ge_operations.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/hash.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/hc128.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/hmac.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/idea.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/integer.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/logging.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/md2.c"
|
||||
#define Transform Transform_md4 // several functions and macros exist multiple times
|
||||
#define AddLength AddLength_md4
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/md4.c"
|
||||
#undef Transform
|
||||
#undef AddLength
|
||||
#define Transform Transform_md5
|
||||
#define AddLength AddLength_md5
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/md5.c"
|
||||
#undef Transform
|
||||
#undef AddLength
|
||||
#undef XTRANSFORM
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/memory.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/misc.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/pkcs7.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/poly1305.c"
|
||||
#undef LO
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/pwdbased.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/rabbit.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/random.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/ripemd.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/rsa.c"
|
||||
#define Transform Transform_sha256
|
||||
#define AddLength AddLength_sha256
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/sha256.c"
|
||||
#undef Ch
|
||||
#undef Maj
|
||||
#undef R
|
||||
#undef R2
|
||||
#undef blk0
|
||||
#undef Transform
|
||||
#undef AddLength
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/sha512.c"
|
||||
#undef Ch
|
||||
#undef Maj
|
||||
#undef R
|
||||
#undef R2
|
||||
#undef blk0
|
||||
#undef XTRANSFORM
|
||||
#define _Transform _Transform_sha
|
||||
#define AddLength AddLength_sha
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/sha.c"
|
||||
#undef Transform
|
||||
#undef AddLength
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/signature.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/srp.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/tfm.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/wc_encrypt.c"
|
||||
#include "wolfssl-3.9.0/wolfcrypt/src/wc_port.c"
|
||||
|
||||
#endif
|
||||
@@ -99,4 +99,12 @@ test()
|
||||
a[0] = 'b';
|
||||
assert_eq(a, "baaaaaaaaaaaaaaa");
|
||||
}
|
||||
|
||||
{
|
||||
arrayview<byte> a((uint8_t*)"123", 3);
|
||||
string b = "["+string(a)+"]";
|
||||
string c = "["+cstring(a)+"]";
|
||||
assert_eq(b, "[123]");
|
||||
assert_eq(c, "[123]");
|
||||
}
|
||||
}
|
||||
479
arlib/string.h
479
arlib/string.h
@@ -1,147 +1,38 @@
|
||||
#pragma once
|
||||
#include "global.h"
|
||||
#include "array.h"
|
||||
#include <string.h>
|
||||
|
||||
//Most strings own their storage; all string do.
|
||||
//If a cstring is constructed from a string, it too owns a reference.
|
||||
//However, if it's created from a char* or .csubstr, it lives and dies by the source array/string.
|
||||
//A cstring created from another cstring mirrors the source.
|
||||
//A string created from a 'soulbound' cstring copies the source. It doesn't care if the other cstring is destroyed.
|
||||
//A string is a mutable sequence of bytes. It usually represents UTF-8 text, but can be arbitrary binary data, including NULs.
|
||||
//All functions taking or returning a char* assume/guarantee NUL termination. However, anything taking uint8_t* does not.
|
||||
|
||||
//Rule of thumb: cstring for arguments, string for storage and return value.
|
||||
//cstring is a special case of string that's not guaranteed to own its storage; it lives and dies by whatever string or array it was created from.
|
||||
//Creating a cstring from another cstring leaves no dependency on this cstring; they're both bound to their source.
|
||||
//Modifying a cstring disconnects it from its source and allows the original string to be deleted.
|
||||
|
||||
class cstring;
|
||||
|
||||
class string {
|
||||
//Reference string implementation - slow but simple
|
||||
//all of these functions, including private functions but not including the members, must be present in a complaint string class
|
||||
#if 0
|
||||
private:
|
||||
char* ptr;
|
||||
size_t len;
|
||||
|
||||
//cstring uses the nocopy and null constructors
|
||||
friend class cstring;
|
||||
|
||||
void init_from(const char * str)
|
||||
{
|
||||
ptr = strdup(str);
|
||||
len = strlen(str);
|
||||
}
|
||||
void init_from(const char * str, uint32_t len)
|
||||
{
|
||||
this->len = len;
|
||||
ptr = malloc(len+1);
|
||||
memcpy(ptr, str, len);
|
||||
ptr[len]='\0';
|
||||
}
|
||||
void init_from(const string& other) { init_from(other.ptr); }
|
||||
void init_from(string&& other) { init_from(other.ptr); }
|
||||
void init_from_nocopy(const char * str) { init_from(str); }
|
||||
void init_from_nocopy(const char * str, uint32_t len) { init_from(str, len); }
|
||||
void init_from_nocopy(const string& other) { init_from(other); }
|
||||
void init_from_nocopy(string&& other) { init_from(other.ptr); }
|
||||
void release() { free(ptr); }
|
||||
|
||||
//constant for all string implementations, but used by the implementation, so let's keep it here
|
||||
int32_t realpos(int32_t pos) const
|
||||
{
|
||||
if (pos >= 0) return pos;
|
||||
else return length()-~pos;
|
||||
}
|
||||
|
||||
char getchar(int32_t index) const { return ptr[realpos(index)]; }
|
||||
void setchar(int32_t index_, char val)
|
||||
{
|
||||
uint32_t index = realpos(index_);
|
||||
if (index==len)
|
||||
{
|
||||
len++;
|
||||
ptr = realloc(ptr, len+1);
|
||||
ptr[len] = 0;
|
||||
}
|
||||
ptr[index] = val;
|
||||
if (val == '\0') len = index;
|
||||
}
|
||||
|
||||
//wstring uses these two plus the public API
|
||||
friend class wstring;
|
||||
bool wcache() const { return false; }
|
||||
void wcache(bool newval) const {}
|
||||
|
||||
public:
|
||||
//NUL terminated
|
||||
const char * data() const { return ptr; }
|
||||
uint32_t length() const { return strlen(ptr); }
|
||||
|
||||
//Non-terminated
|
||||
const char * nt() const { return ptr; }
|
||||
|
||||
void replace(int32_t pos, int32_t len, const string& newdat)
|
||||
{
|
||||
pos = realpos(pos);
|
||||
len = realpos(len);
|
||||
|
||||
const char * part1 = ptr;
|
||||
size_t len1 = pos;
|
||||
|
||||
const char * part2 = newdat.ptr;
|
||||
size_t len2 = newdat.len;
|
||||
|
||||
const char * part3 = ptr+pos+len;
|
||||
size_t len3 = strlen(part3);
|
||||
|
||||
char* newptr = malloc(len1+len2+len3+1);
|
||||
memcpy(newptr, part1, len1);
|
||||
memcpy(newptr+len1, part2, len2);
|
||||
memcpy(newptr+len1+len2, part3, len3);
|
||||
newptr[len1+len2+len3]='\0';
|
||||
|
||||
free(ptr);
|
||||
ptr = newptr;
|
||||
len = len1+len2+len3;
|
||||
}
|
||||
|
||||
string& operator+=(const char * right)
|
||||
{
|
||||
char* ret = malloc(len+strlen(right)+1);
|
||||
memcpy(ret, ptr, len);
|
||||
strcpy(ret+len, right);
|
||||
len += strlen(right);
|
||||
free(ptr);
|
||||
ptr = ret;
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& operator+=(const string& right)
|
||||
{
|
||||
this->operator+=(right.data());
|
||||
return *this;
|
||||
}
|
||||
|
||||
#else
|
||||
//Optimized implementation - fast but unreadable
|
||||
static const int obj_size = 16; // maximum 120, or the inline length overflows
|
||||
// (127 would fit, but that requires an extra alignment byte, which throws the sizeof assert)
|
||||
// minimum 16 (pointer + various members + alignment)
|
||||
// minimum 16 (pointer + various members + alignment) (actually minimum 12 on 32bit, but who needs 32bit)
|
||||
static const int max_inline = obj_size-1;
|
||||
|
||||
union {
|
||||
struct {
|
||||
char m_inline[max_inline];
|
||||
uint8_t m_inline[max_inline];
|
||||
|
||||
//this is how many bytes are unused by the raw string data
|
||||
//if all bytes are used, there are zero unused bytes - which also serves as the NUL
|
||||
//if not inlined, it's -1
|
||||
char m_inline_len;
|
||||
uint8_t m_inline_len;
|
||||
};
|
||||
struct {
|
||||
mutable char* m_data; // if owning, there's also a int32 refcount before this pointer; if not owning, no such thing
|
||||
mutable uint8_t* m_data; // if owning, there's also a int refcount before this pointer; if not owning, no such thing
|
||||
uint32_t m_len;
|
||||
mutable bool m_owning;
|
||||
mutable bool m_nul; // whether the string is properly terminated (always true if owning)
|
||||
mutable bool m_wcache; // could use bitfields here, but no point, there's nothing else I need those extra bytes for
|
||||
char reserved; // matches the last byte of the inline data; never ever access this
|
||||
uint8_t reserved; // matches the last byte of the inline data; never ever access this
|
||||
};
|
||||
};
|
||||
|
||||
@@ -149,16 +40,16 @@ public:
|
||||
{
|
||||
static_assert(sizeof(string)==obj_size);
|
||||
|
||||
return m_inline_len != (char)-1;
|
||||
return m_inline_len != (uint8_t)-1;
|
||||
}
|
||||
|
||||
const char * ptr() const
|
||||
const uint8_t * ptr() const
|
||||
{
|
||||
if (inlined()) return m_inline;
|
||||
else return m_data;
|
||||
}
|
||||
|
||||
char* ptr()
|
||||
uint8_t * ptr()
|
||||
{
|
||||
if (inlined()) return m_inline;
|
||||
else return m_data;
|
||||
@@ -173,11 +64,11 @@ public:
|
||||
//the sizes can be 0 if you want to
|
||||
//sizes are how many characters fit in the string, excluding the NUL
|
||||
//always allocates, doesn't try to inline
|
||||
static char* alloc(char* prev, uint32_t prevsize, uint32_t newsize)
|
||||
static uint8_t* alloc(uint8_t* prev, uint32_t prevsize, uint32_t newsize)
|
||||
{
|
||||
if (prevsize==0)
|
||||
{
|
||||
char* ptr = malloc(bytes_for(newsize));
|
||||
uint8_t* ptr = malloc(bytes_for(newsize));
|
||||
*(int*)ptr = 1;
|
||||
return ptr+sizeof(int);
|
||||
}
|
||||
@@ -196,11 +87,11 @@ public:
|
||||
int* refcount = (int*)(prev-sizeof(int));
|
||||
if (*refcount == 1)
|
||||
{
|
||||
return (char*)realloc(refcount, newsize)+sizeof(int);
|
||||
return (uint8_t*)realloc(refcount, newsize)+sizeof(int);
|
||||
}
|
||||
--*refcount;
|
||||
|
||||
char* ptr = malloc(bytes_for(newsize));
|
||||
uint8_t* ptr = malloc(bytes_for(newsize));
|
||||
memcpy(ptr, prev-sizeof(int), min(prevsize, newsize));
|
||||
*(int*)ptr = 1;
|
||||
return ptr+sizeof(int);
|
||||
@@ -209,11 +100,10 @@ public:
|
||||
|
||||
void unshare() const
|
||||
{
|
||||
wcache(false);
|
||||
if (inlined()) return;
|
||||
if (m_owning && *(int*)(m_data-sizeof(int))==1) return;
|
||||
|
||||
char* prevdat = m_data;
|
||||
uint8_t* prevdat = m_data;
|
||||
m_data = alloc(NULL,0, m_len);
|
||||
memcpy(m_data, prevdat, m_len);
|
||||
m_data[m_len] = '\0';
|
||||
@@ -239,21 +129,20 @@ public:
|
||||
break;
|
||||
case 1: // small->big
|
||||
{
|
||||
char* newptr = alloc(NULL,0, newlen);
|
||||
uint8_t* newptr = alloc(NULL,0, newlen);
|
||||
memcpy(newptr, m_inline, max_inline);
|
||||
newptr[newlen] = '\0';
|
||||
m_data = newptr;
|
||||
m_len = newlen;
|
||||
m_owning = true;
|
||||
m_nul = true;
|
||||
m_wcache = false;
|
||||
|
||||
m_inline_len = -1;
|
||||
}
|
||||
break;
|
||||
case 2: // big->small
|
||||
{
|
||||
char* oldptr = m_data;
|
||||
uint8_t* oldptr = m_data;
|
||||
uint32_t oldlen = m_len;
|
||||
memcpy(m_inline, oldptr, newlen);
|
||||
alloc(oldptr,oldlen, 0);
|
||||
@@ -271,28 +160,28 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
//NUL terminated
|
||||
const char * data() const
|
||||
const char * ptr_withnul() const
|
||||
{
|
||||
if (!inlined() && !m_nul)
|
||||
{
|
||||
unshare();
|
||||
}
|
||||
return ptr();
|
||||
return (char*)ptr();
|
||||
}
|
||||
|
||||
public:
|
||||
uint32_t length() const
|
||||
{
|
||||
if (inlined()) return max_inline-m_inline_len;
|
||||
else return m_len;
|
||||
}
|
||||
|
||||
//Non-terminated (can be terminated in some cases)
|
||||
const char * nt() const
|
||||
arrayview<byte> bytes() const
|
||||
{
|
||||
return ptr();
|
||||
return arrayview<byte>(ptr(), length());
|
||||
}
|
||||
bool hasnt() const // has nul terminator, no missing apostrophe here
|
||||
//If this is true, bytes()[bytes().length()] is '\0'. If false, it's undefined behavior.
|
||||
bool bytes_hasterm() const
|
||||
{
|
||||
return (inlined() || m_nul);
|
||||
}
|
||||
@@ -303,10 +192,13 @@ private:
|
||||
|
||||
void init_from(const char * str)
|
||||
{
|
||||
init_from(str, strlen(str));
|
||||
init_from(arrayview<byte>((uint8_t*)str, strlen(str)));
|
||||
}
|
||||
void init_from(const char * str, uint32_t len)
|
||||
void init_from(arrayview<byte> data)
|
||||
{
|
||||
const uint8_t * str = data.ptr();
|
||||
uint32_t len = data.size();
|
||||
|
||||
if (len <= max_inline)
|
||||
{
|
||||
memcpy(m_inline, str, len);
|
||||
@@ -324,7 +216,6 @@ private:
|
||||
m_len = len;
|
||||
m_owning = true;
|
||||
m_nul = true;
|
||||
m_wcache = false;
|
||||
}
|
||||
}
|
||||
void init_from(const string& other)
|
||||
@@ -344,11 +235,14 @@ private:
|
||||
}
|
||||
void init_from_nocopy(const char * str)
|
||||
{
|
||||
init_from_nocopy(str, strlen(str));
|
||||
init_from_nocopy(arrayview<byte>((uint8_t*)str, strlen(str)));
|
||||
if (!inlined()) m_nul = true;
|
||||
}
|
||||
void init_from_nocopy(const char * str, uint32_t len)
|
||||
void init_from_nocopy(arrayview<byte> data)
|
||||
{
|
||||
const uint8_t * str = data.ptr();
|
||||
uint32_t len = data.size();
|
||||
|
||||
if (len <= max_inline)
|
||||
{
|
||||
memcpy(m_inline, str, len);
|
||||
@@ -359,11 +253,10 @@ private:
|
||||
{
|
||||
m_inline_len = -1;
|
||||
|
||||
m_data = (char*)str;
|
||||
m_data = (uint8_t*)str; // if m_owning is false, we know to not modify this
|
||||
m_len = len;
|
||||
m_owning = false;
|
||||
m_nul = false;
|
||||
m_wcache = false;
|
||||
}
|
||||
}
|
||||
void init_from_nocopy(const string& other)
|
||||
@@ -417,21 +310,9 @@ private:
|
||||
ptr()[index] = val;
|
||||
}
|
||||
|
||||
//wstring uses these two plus the public API
|
||||
friend class wstring;
|
||||
bool wcache() const
|
||||
void append(const uint8_t * newdat, uint32_t newlength)
|
||||
{
|
||||
if (inlined()) return false;
|
||||
else return m_wcache;
|
||||
}
|
||||
void wcache(bool newval) const
|
||||
{
|
||||
if (!inlined()) m_wcache = newval;
|
||||
}
|
||||
|
||||
void append(const char * newdat, uint32_t newlength)
|
||||
{
|
||||
if (newdat >= ptr() && newdat < ptr()+length())
|
||||
if (newdat >= (uint8_t*)ptr() && newdat < (uint8_t*)ptr()+length())
|
||||
{
|
||||
uint32_t offset = newdat-ptr();
|
||||
uint32_t oldlength = length();
|
||||
@@ -447,11 +328,12 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
//Resizes the string to a suitable size, then allows the caller to fill it in with whatever. Contents are undefined.
|
||||
char* construct(uint32_t len)
|
||||
//Resizes the string to a suitable size, then allows the caller to fill it in with whatever. Initial contents are undefined.
|
||||
//The returned pointer may only be used until the first subsequent use of the string, including read-only operations.
|
||||
arrayvieww<byte> construct(uint32_t len)
|
||||
{
|
||||
resize(len);
|
||||
return ptr();
|
||||
return arrayvieww<byte>(ptr(), len);
|
||||
}
|
||||
|
||||
void replace(int32_t pos, int32_t len, const string& newdat) // const string& is ugly, but cstring isn't declared yet.
|
||||
@@ -493,11 +375,11 @@ public:
|
||||
|
||||
if (in.length() != out.length())
|
||||
{
|
||||
char* haystack = ptr();
|
||||
char* haystackend = ptr()+length();
|
||||
uint8_t* haystack = ptr();
|
||||
uint8_t* haystackend = ptr()+length();
|
||||
while (true)
|
||||
{
|
||||
haystack = (char*)memmem(haystack, haystackend-haystack, in.ptr(), in.length());
|
||||
haystack = (uint8_t*)memmem(haystack, haystackend-haystack, in.ptr(), in.length());
|
||||
if (!haystack) break;
|
||||
|
||||
haystack += in.length();
|
||||
@@ -507,13 +389,13 @@ public:
|
||||
}
|
||||
|
||||
string ret;
|
||||
char* retptr = ret.construct(outlen);
|
||||
uint8_t* retptr = ret.construct(outlen).ptr();
|
||||
|
||||
char* prev = ptr();
|
||||
char* myend = ptr()+length();
|
||||
uint8_t* prev = ptr();
|
||||
uint8_t* myend = ptr()+length();
|
||||
while (true)
|
||||
{
|
||||
char* match = (char*)memmem(prev, myend-prev, in.ptr(), in.length());
|
||||
uint8_t* match = (uint8_t*)memmem(prev, myend-prev, in.ptr(), in.length());
|
||||
if (!match) break;
|
||||
|
||||
memcpy(retptr, prev, match-prev);
|
||||
@@ -530,7 +412,7 @@ public:
|
||||
|
||||
string& operator+=(const char * right)
|
||||
{
|
||||
append(right, strlen(right));
|
||||
append((uint8_t*)right, strlen(right));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -542,12 +424,11 @@ public:
|
||||
|
||||
string& operator+=(char right)
|
||||
{
|
||||
append(&right, 1);
|
||||
uint8_t tmp = right;
|
||||
append(&tmp, 1);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
//Shared between all string implementations.
|
||||
private:
|
||||
class noinit {};
|
||||
string(noinit) {}
|
||||
@@ -557,13 +438,14 @@ public:
|
||||
string(const string& other) { init_from(other); }
|
||||
string(string&& other) { init_from(std::move(other)); }
|
||||
string(const char * str) { init_from(str); }
|
||||
string(const char * str, uint32_t len) { init_from(str, len); }
|
||||
//string(const uint8_t * str, uint32_t len) { init_from(str, len); }
|
||||
string(arrayview<uint8_t> bytes) { init_from(bytes); }
|
||||
string& operator=(const string& other) { release(); init_from(other); return *this; }
|
||||
string& operator=(const char * str) { release(); init_from(str); return *this; }
|
||||
~string() { release(); }
|
||||
|
||||
operator bool() const { return length(); }
|
||||
operator const char * () const { return data(); }
|
||||
operator bool() const { return length() != 0; }
|
||||
operator const char * () const { return ptr_withnul(); }
|
||||
|
||||
private:
|
||||
class charref : nocopy {
|
||||
@@ -585,25 +467,58 @@ public:
|
||||
//char operator[](uint32_t index) const { return getchar(index); }
|
||||
char operator[](int index) const { return getchar(index); }
|
||||
|
||||
static string create(const char * data, uint32_t len) { string ret=noinit(); ret.init_from(data, len); return ret; }
|
||||
//static string create(arrayview<uint8_t> data) { string ret=noinit(); ret.init_from(data.ptr(), data.size()); return ret; }
|
||||
|
||||
string substr(int32_t start, int32_t end) const
|
||||
{
|
||||
start = realpos(start);
|
||||
end = realpos(end);
|
||||
return string(data()+start, end-start);
|
||||
return string(arrayview<byte>(ptr()+start, end-start));
|
||||
}
|
||||
inline cstring csubstr(int32_t start, int32_t end) const;
|
||||
inline bool contains(cstring other) const;
|
||||
inline bool startswith(cstring other) const;
|
||||
inline bool endswith(cstring other) const;
|
||||
|
||||
static string codepoint(uint32_t cp)
|
||||
{
|
||||
string ret;
|
||||
if (cp<=0x7F)
|
||||
{
|
||||
ret[0] = cp;
|
||||
}
|
||||
else if (cp<=0x07FF)
|
||||
{
|
||||
ret[0] = (((cp>> 6) )|0xC0);
|
||||
ret[1] = (((cp )&0x3F)|0x80);
|
||||
}
|
||||
else if (cp>=0xD800 && cp<=0xDFFF) return "\xEF\xBF\xBD";
|
||||
else if (cp<=0xFFFF)
|
||||
{
|
||||
ret[0] = (((cp>>12)&0x0F)|0xE0);
|
||||
ret[1] = (((cp>>6 )&0x3F)|0x80);
|
||||
ret[2] = (((cp )&0x3F)|0x80);
|
||||
}
|
||||
else if (cp<=0x10FFFF)
|
||||
{
|
||||
ret[0] = (((cp>>18)&0x07)|0xF0);
|
||||
ret[1] = (((cp>>12)&0x3F)|0x80);
|
||||
ret[2] = (((cp>>6 )&0x3F)|0x80);
|
||||
ret[3] = (((cp )&0x3F)|0x80);
|
||||
}
|
||||
else return "\xEF\xBF\xBD";
|
||||
return ret;
|
||||
}
|
||||
|
||||
//Implementation detail of the equality operators. Don't use.
|
||||
static inline bool s_eq(arrayview<byte> left, arrayview<byte> right)
|
||||
{
|
||||
return (left.size()==right.size() && !memcmp(left.ptr(), right.ptr(), left.size()));
|
||||
}
|
||||
};
|
||||
|
||||
static inline bool string_eq(const char * left, uint32_t leftlen, const char * right, uint32_t rightlen)
|
||||
{
|
||||
return (leftlen==rightlen && !memcmp(left, right, leftlen));
|
||||
}
|
||||
|
||||
inline bool operator==(const string& left, const char * right ) { return string_eq(left.nt(),left.length(), right,strlen(right)); }
|
||||
inline bool operator==(const string& left, const string& right) { return string_eq(left.nt(),left.length(), right.nt(),right.length()); }
|
||||
inline bool operator==(const string& left, const char * right ) { return string::s_eq(left.bytes(), arrayview<byte>((uint8_t*)right,strlen(right))); }
|
||||
inline bool operator==(const string& left, const string& right) { return string::s_eq(left.bytes(), right.bytes()); }
|
||||
inline bool operator==(const char * left, const string& right) { return operator==(right, left); }
|
||||
inline bool operator!=(const string& left, const char * right ) { return !operator==(left, right); }
|
||||
inline bool operator!=(const string& left, const string& right) { return !operator==(left, right); }
|
||||
@@ -628,9 +543,11 @@ public:
|
||||
cstring(string&& other) : string(noinit()) { init_from_nocopy(std::move(other)); }
|
||||
cstring(cstring&& other) : string(noinit()) { init_from_nocopy(std::move(other)); }
|
||||
cstring(const char * str) : string(noinit()) { init_from_nocopy(str); }
|
||||
cstring(const char * str, uint32_t len) : string(noinit()) { init_from_nocopy(str, len); }
|
||||
//cstring(const uint8_t * str, uint32_t len) : string(noinit()) { init_from_nocopy(str, len); }
|
||||
cstring(arrayview<uint8_t> bytes) : string(noinit()) { init_from_nocopy(bytes); }
|
||||
private:
|
||||
cstring(const char * str, uint32_t len, bool nul) : string(noinit()) { init_from_nocopy(str, len); if (!inlined()) m_nul=nul; }
|
||||
//don't use arrayview, if (nul) then it uses len+1 bytes
|
||||
cstring(const uint8_t * str, uint32_t len, bool nul) : string(noinit()) { init_from_nocopy(arrayview<byte>(str, len)); if (!inlined()) m_nul=nul; }
|
||||
public:
|
||||
|
||||
cstring& operator=(const cstring& other) { release(); init_from_nocopy(other); return *this; }
|
||||
@@ -640,96 +557,108 @@ inline cstring string::csubstr(int32_t start, int32_t end) const
|
||||
{
|
||||
start = realpos(start);
|
||||
end = realpos(end);
|
||||
if (inlined()) return cstring(nt()+start, end-start);
|
||||
else return cstring(nt()+start, end-start, (m_nul && (uint32_t)end == m_len));
|
||||
if (inlined()) return cstring(arrayview<byte>(ptr()+start, end-start));
|
||||
else return cstring(ptr()+start, end-start, (m_nul && (uint32_t)end == m_len));
|
||||
}
|
||||
|
||||
inline bool string::contains(cstring other) const
|
||||
{
|
||||
return memmem(this->ptr(), this->length(), other.ptr(), other.length());
|
||||
return memmem(this->ptr(), this->length(), other.ptr(), other.length()) != NULL;
|
||||
}
|
||||
|
||||
inline bool string::startswith(cstring other) const
|
||||
{
|
||||
if (other.length() > this->length()) return false;
|
||||
return (!memcmp(this->ptr(), other.ptr(), other.length()));
|
||||
}
|
||||
|
||||
inline bool string::endswith(cstring other) const
|
||||
{
|
||||
if (other.length() > this->length()) return false;
|
||||
return (!memcmp(this->ptr()+this->length()-other.length(), other.ptr(), other.length()));
|
||||
}
|
||||
|
||||
//TODO
|
||||
class wstring : public string {
|
||||
mutable uint32_t pos_bytes;
|
||||
mutable uint32_t pos_chars;
|
||||
mutable uint32_t wsize;
|
||||
//char pad[4];
|
||||
const uint32_t WSIZE_UNKNOWN = -1;
|
||||
|
||||
void clearcache() const
|
||||
{
|
||||
pos_bytes = 0;
|
||||
pos_chars = 0;
|
||||
wsize = WSIZE_UNKNOWN;
|
||||
wcache(true);
|
||||
}
|
||||
|
||||
void checkcache() const
|
||||
{
|
||||
if (!wcache()) clearcache();
|
||||
}
|
||||
|
||||
uint32_t findcp(int32_t index) const
|
||||
{
|
||||
checkcache();
|
||||
|
||||
if (pos_chars > (uint32_t)index)
|
||||
{
|
||||
pos_bytes=0;
|
||||
pos_chars=0;
|
||||
}
|
||||
|
||||
uint8_t* scan = (uint8_t*)data() + pos_bytes;
|
||||
uint32_t chars = pos_chars;
|
||||
while (chars != (uint32_t)index)
|
||||
{
|
||||
if ((*scan&0xC0) != 0x80) chars++;
|
||||
scan++;
|
||||
}
|
||||
pos_bytes = scan - (uint8_t*)data();
|
||||
pos_chars = index;
|
||||
|
||||
return pos_bytes;
|
||||
}
|
||||
|
||||
uint32_t getcp(int32_t index) const { return 42; }
|
||||
void setcp(int32_t index, uint32_t val) { }
|
||||
|
||||
class charref {
|
||||
wstring* parent;
|
||||
int32_t index;
|
||||
|
||||
public:
|
||||
charref& operator=(char ch) { parent->setcp(index, ch); return *this; }
|
||||
operator uint32_t() { return parent->getcp(index); }
|
||||
|
||||
charref(wstring* parent, int32_t index) : parent(parent), index(index) {}
|
||||
};
|
||||
friend class charref;
|
||||
|
||||
public:
|
||||
wstring() : string() { clearcache(); }
|
||||
wstring(const string& other) : string(other) { clearcache(); }
|
||||
wstring(const char * str) : string(str) { clearcache(); }
|
||||
|
||||
charref operator[](int32_t index) { return charref(this, index); }
|
||||
uint32_t operator[](int32_t index) const { return getcp(index); }
|
||||
|
||||
uint32_t size() const
|
||||
{
|
||||
checkcache();
|
||||
if (wsize == WSIZE_UNKNOWN)
|
||||
{
|
||||
uint8_t* scan = (uint8_t*)data() + pos_bytes;
|
||||
uint32_t chars = pos_chars;
|
||||
while (*scan)
|
||||
{
|
||||
if ((*scan&0xC0) != 0x80) chars++;
|
||||
scan++;
|
||||
}
|
||||
wsize = chars;
|
||||
}
|
||||
return wsize;
|
||||
}
|
||||
};
|
||||
//class wstring : public string {
|
||||
// mutable uint32_t pos_bytes;
|
||||
// mutable uint32_t pos_chars;
|
||||
// mutable uint32_t wsize;
|
||||
// char pad[4];
|
||||
// const uint32_t WSIZE_UNKNOWN = -1;
|
||||
//
|
||||
// void clearcache() const
|
||||
// {
|
||||
// pos_bytes = 0;
|
||||
// pos_chars = 0;
|
||||
// wsize = WSIZE_UNKNOWN;
|
||||
// wcache(true);
|
||||
// }
|
||||
//
|
||||
// void checkcache() const
|
||||
// {
|
||||
// if (!wcache()) clearcache();
|
||||
// }
|
||||
//
|
||||
// uint32_t findcp(int32_t index) const
|
||||
// {
|
||||
// checkcache();
|
||||
//
|
||||
// if (pos_chars > (uint32_t)index)
|
||||
// {
|
||||
// pos_bytes=0;
|
||||
// pos_chars=0;
|
||||
// }
|
||||
//
|
||||
// uint8_t* scan = (uint8_t*)data() + pos_bytes;
|
||||
// uint32_t chars = pos_chars;
|
||||
// while (chars != (uint32_t)index)
|
||||
// {
|
||||
// if ((*scan&0xC0) != 0x80) chars++;
|
||||
// scan++;
|
||||
// }
|
||||
// pos_bytes = scan - (uint8_t*)data();
|
||||
// pos_chars = index;
|
||||
//
|
||||
// return pos_bytes;
|
||||
// }
|
||||
//
|
||||
// uint32_t getcp(int32_t index) const { return 42; }
|
||||
// void setcp(int32_t index, uint32_t val) { }
|
||||
//
|
||||
// class charref {
|
||||
// wstring* parent;
|
||||
// int32_t index;
|
||||
//
|
||||
// public:
|
||||
// charref& operator=(char ch) { parent->setcp(index, ch); return *this; }
|
||||
// operator uint32_t() { return parent->getcp(index); }
|
||||
//
|
||||
// charref(wstring* parent, int32_t index) : parent(parent), index(index) {}
|
||||
// };
|
||||
// friend class charref;
|
||||
//
|
||||
//public:
|
||||
// wstring() : string() { clearcache(); }
|
||||
// wstring(const string& other) : string(other) { clearcache(); }
|
||||
// wstring(const char * str) : string(str) { clearcache(); }
|
||||
//
|
||||
// charref operator[](int32_t index) { return charref(this, index); }
|
||||
// uint32_t operator[](int32_t index) const { return getcp(index); }
|
||||
//
|
||||
// uint32_t size() const
|
||||
// {
|
||||
// checkcache();
|
||||
// if (wsize == WSIZE_UNKNOWN)
|
||||
// {
|
||||
// uint8_t* scan = (uint8_t*)data() + pos_bytes;
|
||||
// uint32_t chars = pos_chars;
|
||||
// while (*scan)
|
||||
// {
|
||||
// if ((*scan&0xC0) != 0x80) chars++;
|
||||
// scan++;
|
||||
// }
|
||||
// wsize = chars;
|
||||
// }
|
||||
// return wsize;
|
||||
// }
|
||||
//};
|
||||
|
||||
65
arlib/stringconv.cpp
Normal file
65
arlib/stringconv.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "stringconv.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
#define FROMFUNC(t,frt,f) \
|
||||
bool fromstring(cstring s, t& out) \
|
||||
{ \
|
||||
out = 0; \
|
||||
char * tmp; /* odd that this one isn't overloaded, like strchr */ \
|
||||
frt ret = f(s, &tmp, 10); \
|
||||
if (*tmp || (t)ret != (frt)ret) return false; \
|
||||
out = ret; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
FROMFUNC(signed char, long, strtol)
|
||||
FROMFUNC(unsigned char, unsigned long, strtoul)
|
||||
FROMFUNC(signed short, long, strtol)
|
||||
FROMFUNC(unsigned short, unsigned long, strtoul)
|
||||
FROMFUNC(signed int, long, strtol)
|
||||
FROMFUNC(unsigned int, unsigned long, strtoul)
|
||||
FROMFUNC(signed long, long, strtol)
|
||||
FROMFUNC(unsigned long, unsigned long, strtoul)
|
||||
FROMFUNC(signed long long, long long, strtoll)
|
||||
FROMFUNC(unsigned long long, unsigned long long, strtoull)
|
||||
|
||||
bool fromstring(cstring s, double& out)
|
||||
{
|
||||
out = 0;
|
||||
char * tmp;
|
||||
double ret = strtod(s, &tmp);
|
||||
if (*tmp || ret==HUGE_VAL || ret==-HUGE_VAL) return false;
|
||||
out = ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
//strtof exists in C99, but let's not use that
|
||||
bool fromstring(cstring s, float& out)
|
||||
{
|
||||
out = 0;
|
||||
double tmp;
|
||||
if (!fromstring(s, tmp)) return false;
|
||||
if (tmp < -FLT_MAX || tmp > FLT_MAX) return false;
|
||||
out = tmp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fromstring(cstring s, bool& out)
|
||||
{
|
||||
if (s=="false" || s=="0")
|
||||
{
|
||||
out=false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (s=="true" || s=="1")
|
||||
{
|
||||
out=true;
|
||||
return true;
|
||||
}
|
||||
|
||||
out=false;
|
||||
return false;
|
||||
}
|
||||
@@ -1,20 +1,74 @@
|
||||
#pragma once
|
||||
#include "global.h"
|
||||
#include "string.h"
|
||||
#include <stdio.h>
|
||||
|
||||
inline string tostring(string s) { return s; }
|
||||
inline string tostring(cstring s) { return s; }
|
||||
inline string tostring(const char * s) { return s; }
|
||||
inline string tostring(int val) { char ret[16]; sprintf(ret, "%i", val); return ret; }
|
||||
//I'd use int123_t, but the set {8, 16, 32, 64} is smaller than {char, short, int, long, long long}, so one disappears
|
||||
//if this one shows up (for example time_t = long on Windows), error
|
||||
//printf has PRIi32, but the native ones are defined in terms of int/long
|
||||
inline string tostring( signed char val) { char ret[32]; sprintf(ret, "%i", val); return ret; } // the C++ standard says
|
||||
inline string tostring(unsigned char val) { char ret[32]; sprintf(ret, "%u", val); return ret; } // (un)signed char/short are
|
||||
//signless char isn't integral, so not here
|
||||
inline string tostring( signed short val) { char ret[32]; sprintf(ret, "%i", val); return ret; } // promoted to (un)signed int
|
||||
inline string tostring(unsigned short val) { char ret[32]; sprintf(ret, "%u", val); return ret; } // in ellipsis
|
||||
inline string tostring( signed int val) { char ret[32]; sprintf(ret, "%i", val); return ret; }
|
||||
inline string tostring(unsigned int val) { char ret[32]; sprintf(ret, "%u", val); return ret; }
|
||||
inline string tostring( signed long val) { char ret[32]; sprintf(ret, "%li", val); return ret; }
|
||||
inline string tostring(unsigned long val) { char ret[32]; sprintf(ret, "%lu", val); return ret; }
|
||||
#ifdef _WIN32
|
||||
# ifdef __GNUC__ // my GCC doesn't recognize I64
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wformat"
|
||||
# endif
|
||||
inline string tostring( signed long long val) { char ret[32]; sprintf(ret, "%I64i", val); return ret; }
|
||||
inline string tostring(unsigned long long val) { char ret[32]; sprintf(ret, "%I64u", val); return ret; }
|
||||
# ifdef __GNUC__
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#else
|
||||
inline string tostring( signed long long val) { char ret[32]; sprintf(ret, "%lli", val); return ret; }
|
||||
inline string tostring(unsigned long long val) { char ret[32]; sprintf(ret, "%llu", val); return ret; }
|
||||
#endif
|
||||
inline string tostring(float val) { char ret[64]; sprintf(ret, "%f", val); return ret; } // increase buffer sizes
|
||||
inline string tostring(double val) { char ret[1024]; sprintf(ret, "%f", val); return ret; } // http://stackoverflow.com/q/7235456
|
||||
inline string tostring(bool val) { return val ? "true" : "false"; }
|
||||
//inline string tostring(char val); // not sure if this one makes sense
|
||||
|
||||
template<typename T> inline T fromstring(cstring s);
|
||||
template<> inline string fromstring<string>(cstring s) { return s; }
|
||||
template<> inline cstring fromstring<cstring>(cstring s) { return s; }
|
||||
//no const char *, their lifetime is unknowable
|
||||
inline string tostring(const char * s) { return s; } // only exists as tostring, fromstring would be a memory leak
|
||||
|
||||
template<> inline int fromstring<int>(cstring s) { return strtol(s, NULL, 0); }
|
||||
template<> inline long int fromstring<long int>(cstring s) { return strtol(s, NULL, 0); }
|
||||
template<> inline unsigned int fromstring<unsigned int>(cstring s) { return strtoul(s, NULL, 0); }
|
||||
template<> inline float fromstring<float>(cstring s) { return strtod(s, NULL); }
|
||||
|
||||
template<> inline char fromstring<char>(cstring s) { return s[0]; }
|
||||
template<> inline bool fromstring<bool>(cstring s) { return s=="true"; }
|
||||
inline bool fromstring(cstring s, string& out) { out=s; return true; }
|
||||
inline bool fromstring(cstring s, cstring& out) { out=s; return true; }
|
||||
bool fromstring(cstring s, signed char & out);
|
||||
bool fromstring(cstring s, unsigned char & out);
|
||||
bool fromstring(cstring s, signed short & out);
|
||||
bool fromstring(cstring s, unsigned short & out);
|
||||
bool fromstring(cstring s, signed int & out);
|
||||
bool fromstring(cstring s, unsigned int & out);
|
||||
bool fromstring(cstring s, signed long & out);
|
||||
bool fromstring(cstring s, unsigned long & out);
|
||||
bool fromstring(cstring s, signed long long & out);
|
||||
bool fromstring(cstring s, unsigned long long & out);
|
||||
bool fromstring(cstring s, float& out);
|
||||
bool fromstring(cstring s, double& out);
|
||||
bool fromstring(cstring s, bool& out);
|
||||
|
||||
|
||||
#define ALLSTRINGABLE(x) \
|
||||
x(string) \
|
||||
x(cstring) \
|
||||
x(signed char) \
|
||||
x(unsigned char) \
|
||||
x(signed short) \
|
||||
x(unsigned short) \
|
||||
x(signed int) \
|
||||
x(unsigned int) \
|
||||
x(signed long) \
|
||||
x(unsigned long) \
|
||||
x(signed long long) \
|
||||
x(unsigned long long) \
|
||||
x(float) \
|
||||
x(double) \
|
||||
x(bool)
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
#ifdef ARLIB_TEST
|
||||
#include "test.h"
|
||||
#include "array.h"
|
||||
#include "gui/window.h"
|
||||
|
||||
struct testlist {
|
||||
void(*func)();
|
||||
const char * loc;
|
||||
const char * name;
|
||||
testlist* next;
|
||||
};
|
||||
|
||||
static testlist* g_testlist;
|
||||
|
||||
_testdecl::_testdecl(void(*func)(), const char * name)
|
||||
_testdecl::_testdecl(void(*func)(), const char * loc, const char * name)
|
||||
{
|
||||
testlist* next = malloc(sizeof(testlist));
|
||||
next->func = func;
|
||||
next->loc = loc;
|
||||
next->name = name;
|
||||
next->next = g_testlist;
|
||||
g_testlist = next;
|
||||
@@ -28,9 +31,9 @@ static string stack(int top)
|
||||
{
|
||||
string ret = " (line "+tostring(top);
|
||||
|
||||
for (int i=callstack.size();i>=0;i--)
|
||||
for (int i=callstack.size()-1;i>=0;i--)
|
||||
{
|
||||
ret += " from "+tostring(callstack[i]);
|
||||
ret += ", called from "+tostring(callstack[i]);
|
||||
}
|
||||
|
||||
return ret+")";
|
||||
@@ -62,6 +65,12 @@ void _testeqfail(cstring name, int line, cstring expected, cstring actual)
|
||||
#undef main // the real main is #define'd to something stupid on test runs
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
#ifndef ARGUI_NONE
|
||||
window_init(&argc, &argv);
|
||||
#else
|
||||
_window_init_file();
|
||||
#endif
|
||||
|
||||
int count[2]={0,0};
|
||||
|
||||
//flip list backwards
|
||||
@@ -80,9 +89,11 @@ int main(int argc, char* argv[])
|
||||
while (test)
|
||||
{
|
||||
testlist* next = test->next;
|
||||
printf("Testing %s...", test->name);
|
||||
if (test->name) printf("Testing %s (%s)...", test->name, test->loc);
|
||||
else printf("Testing %s...", test->loc);
|
||||
fflush(stdout);
|
||||
thisfail = false;
|
||||
callstack.reset();
|
||||
test->func();
|
||||
count[thisfail]++;
|
||||
if (!thisfail) puts(" pass");
|
||||
|
||||
25
arlib/test.h
25
arlib/test.h
@@ -6,9 +6,17 @@
|
||||
|
||||
#ifdef ARLIB_TEST
|
||||
|
||||
class _test_maybeptr {
|
||||
const char * data;
|
||||
public:
|
||||
_test_maybeptr() : data(NULL) {}
|
||||
_test_maybeptr(const char * data) : data(data) {}
|
||||
|
||||
operator const char *() { return data; }
|
||||
};
|
||||
class _testdecl {
|
||||
public:
|
||||
_testdecl(void(*func)(), const char * name);
|
||||
_testdecl(void(*func)(), const char * loc, const char * name);
|
||||
};
|
||||
|
||||
void _testfail(cstring name, int line);
|
||||
@@ -18,15 +26,16 @@ void _teststack_push(int line);
|
||||
void _teststack_pop();
|
||||
|
||||
#define TESTFUNCNAME JOIN(_testfunc, __LINE__)
|
||||
#define test() \
|
||||
#define test(...) \
|
||||
static void TESTFUNCNAME(); \
|
||||
static _testdecl JOIN(_testdecl, __LINE__)(TESTFUNCNAME, __FILE__ ":" STR(__LINE__)); \
|
||||
static _testdecl JOIN(_testdecl, __LINE__)(TESTFUNCNAME, __FILE__ ":" STR(__LINE__), _test_maybeptr(__VA_ARGS__)); \
|
||||
static void TESTFUNCNAME()
|
||||
#define assert(x) do { if (!(x)) { _testfail("\nFailed assertion " #x, __LINE__); return; } } while(0)
|
||||
#define assert_eq(x,y) do { \
|
||||
if ((x) != (y)) \
|
||||
#define assert_ret(x, ret) do { if (!(x)) { _testfail("\nFailed assertion " #x, __LINE__); return ret; } } while(0)
|
||||
#define assert(x) assert_ret(x,)
|
||||
#define assert_eq(actual,expected) do { \
|
||||
if ((actual) != (expected)) \
|
||||
{ \
|
||||
_testeqfail(#x " == " #y, __LINE__, tostring(y), tostring(x)); \
|
||||
_testeqfail(#actual " == " #expected, __LINE__, tostring(expected), tostring(actual)); \
|
||||
return; \
|
||||
} \
|
||||
} while(0)
|
||||
@@ -34,7 +43,7 @@ void _teststack_pop();
|
||||
|
||||
#else
|
||||
|
||||
#define test() static void MAYBE_UNUSED JOIN(_testfunc_, __LINE__)()
|
||||
#define test(...) static void MAYBE_UNUSED JOIN(_testfunc_, __LINE__)()
|
||||
#define assert(x)
|
||||
#define assert_eq(x,y)
|
||||
#define testcall(x) x
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
#else
|
||||
|
||||
//https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html
|
||||
//the memory model remains unused, but all functions must still be defined.
|
||||
//the memory model isn't used, but all functions must still be defined.
|
||||
#define LOCKD_LOCKS_MODEL(type, modelname) \
|
||||
inline type lock_incr ## modelname(type * val) { __sync_fetch_and_add(val, 1); } \
|
||||
inline type lock_decr ## modelname(type * val) { __sync_fetch_and_sub(val, 1); } \
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "thread.h"
|
||||
|
||||
#ifdef ARLIB_THREAD
|
||||
//a nonatomic read to an atomic variable is safe only if correct results are guaranteed if any old value is read
|
||||
//a write of non-NULL and non-tag is guaranteed to be the final write, and if anything else seems to be there, we do an atomic read
|
||||
void* thread_once_undo_core(void* * item, function<void*()> calculate, function<void(void*)> undo)
|
||||
@@ -61,3 +62,4 @@ void* thread_once_core(void* * item, function<void*()> calculate)
|
||||
return *item;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "thread.h"
|
||||
|
||||
#ifdef ARLIB_THREAD
|
||||
namespace {
|
||||
|
||||
//TODO: there is no procedure for destroying threads
|
||||
@@ -91,3 +92,4 @@ void thread_split(unsigned int count, function<void(unsigned int id)> work)
|
||||
done->wait(count-1);
|
||||
delete done;
|
||||
}
|
||||
#endif
|
||||
@@ -8,8 +8,8 @@
|
||||
//A thread is rather heavy; for short-running jobs, use thread_create_short or thread_split.
|
||||
void thread_create(function<void()> start);
|
||||
|
||||
//Returns the number of threads to create to utilize the system resources optimally.
|
||||
unsigned int thread_num_cores();
|
||||
////Returns the number of threads to create to utilize the system resources optimally.
|
||||
//unsigned int thread_num_cores();
|
||||
|
||||
#include "atomic.h"
|
||||
#include <string.h>
|
||||
@@ -71,46 +71,46 @@ public:
|
||||
};
|
||||
|
||||
|
||||
//Some shenanigans: gcc throws errors about strict-aliasing rules if I don't force its hand, and most
|
||||
// implementations aren't correctly optimized (they leave copies on the stack).
|
||||
//This is one of few that confuse the optimizer exactly as much as I want.
|
||||
template<typename T> char* allow_alias(T* ptr) { return (char*)ptr; }
|
||||
|
||||
//Executes 'calculate' exactly once. The return value is stored in 'item'. If multiple threads call
|
||||
// this simultaneously, none returns until calculate() is done.
|
||||
//'item' must be initialized to NULL. calculate() must return a valid pointer to an object.
|
||||
// 'return new mutex;' is valid, as is returning the address of something static.
|
||||
//Non-pointers, such as (void*)1, are not allowed.
|
||||
//Returns *item.
|
||||
void* thread_once_core(void* * item, function<void*()> calculate);
|
||||
|
||||
template<typename T> T* thread_once(T* * item, function<T*()> calculate)
|
||||
{
|
||||
return (T*)thread_once_core((void**)item, *(function<void*()>*)allow_alias(&calculate));
|
||||
}
|
||||
|
||||
//This is like thread_once, but calculate() can be called multiple times. If this happens, undo()
|
||||
//will be called for all except one; the last one will be returned.
|
||||
void* thread_once_undo_core(void* * item, function<void*()> calculate, function<void(void*)> undo);
|
||||
|
||||
template<typename T> T* thread_once_undo(T* * item, function<T*()> calculate, function<void(T*)> undo)
|
||||
{
|
||||
return (T*)thread_once_undo_core((void**)item,
|
||||
*(function<void*()>*)allow_alias(&calculate),
|
||||
*(function<void(void*)>*)allow_alias(&undo));
|
||||
}
|
||||
|
||||
|
||||
//This function is a workaround for a GCC bug. Don't call it yourself.
|
||||
template<void*(*create)(), void(*undo)(void*)> void* thread_once_create_gccbug(void* * item)
|
||||
{
|
||||
return thread_once_undo(item, bind(create), bind(undo));
|
||||
}
|
||||
//Simple convenience function, just calls the above.
|
||||
template<typename T> T* thread_once_create(T* * item)
|
||||
{
|
||||
return (T*)thread_once_create_gccbug<generic_new_void<T>, generic_delete_void<T> >((void**)item);
|
||||
}
|
||||
////Some shenanigans: gcc throws errors about strict-aliasing rules if I don't force its hand, and most
|
||||
//// implementations aren't correctly optimized (they leave copies on the stack).
|
||||
////This is one of few that confuse the optimizer exactly as much as I want.
|
||||
//template<typename T> char* allow_alias(T* ptr) { return (char*)ptr; }
|
||||
//
|
||||
////Executes 'calculate' exactly once. The return value is stored in 'item'. If multiple threads call
|
||||
//// this simultaneously, none returns until calculate() is done.
|
||||
////'item' must be initialized to NULL. calculate() must return a valid pointer to an object.
|
||||
//// 'return new mutex;' is valid, as is returning the address of something static.
|
||||
////Non-pointers, such as (void*)1, are not allowed.
|
||||
////Returns *item.
|
||||
//void* thread_once_core(void* * item, function<void*()> calculate);
|
||||
//
|
||||
//template<typename T> T* thread_once(T* * item, function<T*()> calculate)
|
||||
//{
|
||||
// return (T*)thread_once_core((void**)item, *(function<void*()>*)allow_alias(&calculate));
|
||||
//}
|
||||
//
|
||||
////This is like thread_once, but calculate() can be called multiple times. If this happens, undo()
|
||||
////will be called for all except one; the last one will be returned.
|
||||
//void* thread_once_undo_core(void* * item, function<void*()> calculate, function<void(void*)> undo);
|
||||
//
|
||||
//template<typename T> T* thread_once_undo(T* * item, function<T*()> calculate, function<void(T*)> undo)
|
||||
//{
|
||||
// return (T*)thread_once_undo_core((void**)item,
|
||||
// *(function<void*()>*)allow_alias(&calculate),
|
||||
// *(function<void(void*)>*)allow_alias(&undo));
|
||||
//}
|
||||
//
|
||||
//
|
||||
////This function is a workaround for a GCC bug. Don't call it yourself.
|
||||
//template<void*(*create)(), void(*undo)(void*)> void* thread_once_create_gccbug(void* * item)
|
||||
//{
|
||||
// return thread_once_undo(item, bind(create), bind(undo));
|
||||
//}
|
||||
////Simple convenience function, just calls the above.
|
||||
//template<typename T> T* thread_once_create(T* * item)
|
||||
//{
|
||||
// return (T*)thread_once_create_gccbug<generic_new_void<T>, generic_delete_void<T> >((void**)item);
|
||||
//}
|
||||
|
||||
|
||||
class mutexlocker : nocopy {
|
||||
@@ -190,7 +190,7 @@ void thread_split(unsigned int count, function<void(unsigned int id)> work);
|
||||
|
||||
#else
|
||||
|
||||
//Some parts of arlib want to work with threads disabled.
|
||||
//Some parts of Arlib want to work with threads disabled.
|
||||
class mutex : nocopy {
|
||||
public:
|
||||
void lock() {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "thread.h"
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32) && defined(ARLIB_THREAD)
|
||||
#undef bind
|
||||
#include <windows.h>
|
||||
#define bind bind_func
|
||||
28
arlib/tuple.h
Normal file
28
arlib/tuple.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include "global.h"
|
||||
|
||||
template<typename Thead, typename... Ttail> class tuple_impl {
|
||||
template<typename...> friend class tuple;
|
||||
template<typename, typename...> friend class tuple_impl;
|
||||
|
||||
Thead head;
|
||||
tuple_impl<Ttail...> tail;
|
||||
|
||||
tuple_impl(Thead head, Ttail... tails) : head(head), tail(tails...) {}
|
||||
};
|
||||
|
||||
template<typename Thead> class tuple_impl<Thead> {
|
||||
template<typename...> friend class tuple;
|
||||
template<typename, typename...> friend class tuple_impl;
|
||||
|
||||
Thead head;
|
||||
tuple_impl(Thead head) : head(head) {}
|
||||
};
|
||||
|
||||
template<class... Ts> class tuple {
|
||||
tuple_impl<Ts...> m_data;
|
||||
public:
|
||||
tuple(Ts... args) : m_data(args...) {}
|
||||
};
|
||||
|
||||
template<> class tuple<> {};
|
||||
@@ -1 +0,0 @@
|
||||
#include "wutf/wutf.h"
|
||||
@@ -20,7 +20,7 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
//The above license applies only to this file, not the entire Arlib.
|
||||
//The above license applies only to the WuTF directory, not the entire Arlib.
|
||||
|
||||
//See wutf.h for documentation.
|
||||
|
||||
@@ -365,7 +365,7 @@ int WuTF_utf16_to_utf8(int flags, const uint16_t* utf16, int utf16_len, char* ut
|
||||
if (head <= 0x7F)
|
||||
{
|
||||
if (oat+1 > oend) break;
|
||||
*oat++ = head;
|
||||
*oat++ = (uint8_t)head;
|
||||
}
|
||||
else if (head <= 0x07FF)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
//The above license applies only to this file, not the entire Arlib.
|
||||
//The above license applies only to the WuTF directory, not the entire Arlib.
|
||||
|
||||
#if 0
|
||||
//You don't need this. It's just a bunch of tests for WuTF itself.
|
||||
//To run: Flip the above #if, then
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
//The above license applies only to this file, not the entire Arlib.
|
||||
//The above license applies only to the WuTF directory, not the entire Arlib.
|
||||
|
||||
//See wutf.h for documentation.
|
||||
|
||||
@@ -159,6 +159,7 @@ void WuTF_redirect_function(WuTF_funcptr victim, WuTF_funcptr replacement)
|
||||
DWORD prot;
|
||||
//it's usually considered bad to have W+X on the same page, but the alternative is risking
|
||||
// removing X from VirtualProtect or NtProtectVirtualMemory, and then I can't fix it.
|
||||
//alternatively, it could make C do W; e.
|
||||
//it doesn't matter, anyways; we (should be) called so early no hostile input has been processed
|
||||
// yet, and even if hostile code is running, it can just wait until I put back X.
|
||||
VirtualProtect((void*)victim, 64, PAGE_EXECUTE_READWRITE, &prot);
|
||||
@@ -235,9 +236,9 @@ void WuTF_args(int* argc_p, char** * argv_p)
|
||||
|
||||
for (i=0;i<argc;i++)
|
||||
{
|
||||
int cb = WuTF_utf16_to_utf8(0, (uint16_t*)wargv[i], -1, NULL, 0);
|
||||
int cb = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, NULL, NULL);
|
||||
argv[i] = (char*)HeapAlloc(GetProcessHeap(), 0, cb);
|
||||
WuTF_utf16_to_utf8(0, (uint16_t*)wargv[i], -1, argv[i], cb);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], cb, NULL, NULL);
|
||||
}
|
||||
argv[argc]=0;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
//The above license applies only to this file, not the entire Arlib.
|
||||
//The above license applies only to the WuTF directory, not the entire Arlib.
|
||||
|
||||
// It is well known that Windows supports two flavors of every function that
|
||||
// takes or returns strings(*): A and W. The A ones take strings in the local
|
||||
@@ -65,8 +65,6 @@
|
||||
//- CharNextA/etc are unchanged and still expect the ANSI code page. (Does anything ever use them?)
|
||||
//- SetFileApisToOEM is untested. I don't know if it's ignored or if it actually does set them to
|
||||
// OEM. Either way, the fix is easy: don't use it.
|
||||
//- Actually uses WTF-8 <https://simonsapin.github.io/wtf-8/>; you may see the surrogate characters
|
||||
// if you somehow get invalid UTF-16 (it's fairly permissive on UTF8->16, too; it accepts CESU-8)
|
||||
//- Windows filenames are limited to ~260 characters; but I believe functions that return filenames
|
||||
// will count the UTF-8 bytes. (The ones taking filename inputs should work up to 260 UTF-16
|
||||
// codepoints.)
|
||||
|
||||
579
arlib/zip.cpp
Normal file
579
arlib/zip.cpp
Normal file
@@ -0,0 +1,579 @@
|
||||
#define timegm timegm_goaway
|
||||
#include "zip.h"
|
||||
#include "test.h"
|
||||
#include "crc32.h"
|
||||
#include "endian.h"
|
||||
#define MINIZ_HEADER_FILE_ONLY
|
||||
#include "deps/miniz.c"
|
||||
#include <time.h>
|
||||
#undef timegm
|
||||
|
||||
//files in directories are normal files with / in the name
|
||||
//directories themselves are represented as size-0 files with names ending with /, no special flags except minimum version
|
||||
|
||||
//similar to mktime, but UTC timezone
|
||||
//from http://stackoverflow.com/a/11324281
|
||||
static time_t timegm(register struct tm * t)
|
||||
/* struct tm to seconds since Unix epoch */
|
||||
{
|
||||
register long year;
|
||||
register time_t result;
|
||||
#define MONTHSPERYEAR 12 /* months per calendar year */
|
||||
static const int cumdays[MONTHSPERYEAR] =
|
||||
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
|
||||
|
||||
/*@ +matchanyintegral @*/
|
||||
year = 1900 + t->tm_year + t->tm_mon / MONTHSPERYEAR;
|
||||
result = (year - 1970) * 365 + cumdays[t->tm_mon % MONTHSPERYEAR];
|
||||
result += (year - 1968) / 4;
|
||||
result -= (year - 1900) / 100;
|
||||
result += (year - 1600) / 400;
|
||||
if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0) &&
|
||||
(t->tm_mon % MONTHSPERYEAR) < 2)
|
||||
result--;
|
||||
result += t->tm_mday - 1;
|
||||
result *= 24;
|
||||
result += t->tm_hour;
|
||||
result *= 60;
|
||||
result += t->tm_min;
|
||||
result *= 60;
|
||||
result += t->tm_sec;
|
||||
if (t->tm_isdst == 1)
|
||||
result -= 3600;
|
||||
/*@ -matchanyintegral @*/
|
||||
return (result);
|
||||
}
|
||||
|
||||
#ifdef _WIN32 // surprisingly, this is safe - gmtime() returns a thread local
|
||||
#define gmtime_r(a,b) (*(b)=*gmtime(a))
|
||||
#endif
|
||||
|
||||
static time_t fromdosdate(uint32_t date)
|
||||
{
|
||||
if (!date) return 0;
|
||||
|
||||
struct tm tp = {
|
||||
/*tm_sec*/ (int)((date>>0)&31)<<1,
|
||||
/*tm_min*/ (int)(date>>5)&63,
|
||||
/*tm_hour*/ (int)(date>>11)&31,
|
||||
/*tm_mday*/ (int)(date>>16>>0)&31,
|
||||
/*tm_mon*/ (int)((date>>16>>5)&15) - 1,
|
||||
/*tm_year*/ (int)(date>>16>>9) + 1980 - 1900,
|
||||
/*tm_wday*/ 0,
|
||||
/*tm_yday*/ 0,
|
||||
/*tm_is_dst*/ false,
|
||||
};
|
||||
return timegm(&tp);
|
||||
}
|
||||
|
||||
static uint32_t todosdate(time_t date)
|
||||
{
|
||||
if (!date) return 0;
|
||||
|
||||
struct tm tp;
|
||||
gmtime_r(&date, &tp);
|
||||
|
||||
return (tp.tm_year-1980+1900)<<9<<16 |
|
||||
(tp.tm_mon+1)<<5<<16 |
|
||||
tp.tm_mday<<0<<16 |
|
||||
tp.tm_hour<<11 |
|
||||
tp.tm_min<<5 |
|
||||
tp.tm_sec>>1;
|
||||
}
|
||||
|
||||
static uint16_t cp437[256]={
|
||||
0x0000,0x263A,0x263B,0x2665,0x2666,0x2663,0x2660,0x2022,0x25D8,0x25CB,0x25D9,0x2642,0x2640,0x266A,0x266B,0x263C,
|
||||
0x25BA,0x25C4,0x2195,0x203C,0x00B6,0x00A7,0x25AC,0x21A8,0x2191,0x2193,0x2192,0x2190,0x221F,0x2194,0x25B2,0x25BC,
|
||||
0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002A,0x002B,0x002C,0x002D,0x002E,0x002F,
|
||||
0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003A,0x003B,0x003C,0x003D,0x003E,0x003F,
|
||||
0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004A,0x004B,0x004C,0x004D,0x004E,0x004F,
|
||||
0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005A,0x005B,0x005C,0x005D,0x005E,0x005F,
|
||||
0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006A,0x006B,0x006C,0x006D,0x006E,0x006F,
|
||||
0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007A,0x007B,0x007C,0x007D,0x007E,0x2302,
|
||||
0x00C7,0x00FC,0x00E9,0x00E2,0x00E4,0x00E0,0x00E5,0x00E7,0x00EA,0x00EB,0x00E8,0x00EF,0x00EE,0x00EC,0x00C4,0x00C5,
|
||||
0x00C9,0x00E6,0x00C6,0x00F4,0x00F6,0x00F2,0x00FB,0x00F9,0x00FF,0x00D6,0x00DC,0x00A2,0x00A3,0x00A5,0x20A7,0x0192,
|
||||
0x00E1,0x00ED,0x00F3,0x00FA,0x00F1,0x00D1,0x00AA,0x00BA,0x00BF,0x2310,0x00AC,0x00BD,0x00BC,0x00A1,0x00AB,0x00BB,
|
||||
0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255D,0x255C,0x255B,0x2510,
|
||||
0x2514,0x2534,0x252C,0x251C,0x2500,0x253C,0x255E,0x255F,0x255A,0x2554,0x2569,0x2566,0x2560,0x2550,0x256C,0x2567,
|
||||
0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256B,0x256A,0x2518,0x250C,0x2588,0x2584,0x258C,0x2590,0x2580,
|
||||
0x03B1,0x00DF,0x0393,0x03C0,0x03A3,0x03C3,0x00B5,0x03C4,0x03A6,0x0398,0x03A9,0x03B4,0x221E,0x03C6,0x03B5,0x2229,
|
||||
0x2261,0x00B1,0x2265,0x2264,0x2320,0x2321,0x00F7,0x2248,0x00B0,0x2219,0x00B7,0x221A,0x207F,0x00B2,0x25A0,0x00A0,
|
||||
};
|
||||
static string fromcp437(arrayview<byte> bytes)
|
||||
{
|
||||
string out;
|
||||
for (byte b : bytes)
|
||||
{
|
||||
out += string::codepoint(cp437[b]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
struct zip::locfhead {
|
||||
litend<uint32_t> signature;
|
||||
static const uint32_t signature_expected = 0x04034B50;
|
||||
litend<uint16_t> vermin;
|
||||
litend<uint16_t> bitflags;
|
||||
litend<uint16_t> compmethod;
|
||||
//litend<uint16_t> modtime;
|
||||
//litend<uint16_t> moddate; // merging these
|
||||
litend<uint32_t> moddate;
|
||||
litend<uint32_t> crc32;
|
||||
litend<uint32_t> size_comp;
|
||||
litend<uint32_t> size_decomp;
|
||||
litend<uint16_t> len_fname;
|
||||
litend<uint16_t> len_exfield;
|
||||
};
|
||||
|
||||
struct zip::centdirrec {
|
||||
litend<uint32_t> signature;
|
||||
static const uint32_t signature_expected = 0x02014B50;
|
||||
litend<uint16_t> verused;
|
||||
litend<uint16_t> vermin;
|
||||
litend<uint16_t> bitflags;
|
||||
litend<uint16_t> compmethod;
|
||||
//litend<uint16_t> modtime;
|
||||
//litend<uint16_t> moddate; // merging these
|
||||
litend<uint32_t> moddate;
|
||||
litend<uint32_t> crc32;
|
||||
litend<uint32_t> size_comp;
|
||||
litend<uint32_t> size_decomp;
|
||||
litend<uint16_t> len_fname;
|
||||
litend<uint16_t> len_exfield;
|
||||
litend<uint16_t> len_fcomment;
|
||||
litend<uint16_t> disknr;
|
||||
litend<uint16_t> attr_int;
|
||||
litend<uint32_t> attr_ext;
|
||||
litend<uint32_t> header_start;
|
||||
};
|
||||
|
||||
struct zip::endofcdr {
|
||||
litend<uint32_t> signature;
|
||||
static const uint32_t signature_expected = 0x06054B50;
|
||||
litend<uint16_t> diskid_this;
|
||||
litend<uint16_t> diskid_cdrst;
|
||||
litend<uint16_t> numfiles_thisdisk;
|
||||
litend<uint16_t> numfiles;
|
||||
litend<uint32_t> cdrsize;
|
||||
litend<uint32_t> cdrstart_fromdisk;
|
||||
litend<uint16_t> zipcommentlen;
|
||||
};
|
||||
|
||||
zip::endofcdr* zip::getendofcdr(arrayview<byte> data)
|
||||
{
|
||||
//must be somewhere in zip::, they're private
|
||||
static_assert(sizeof(zip::locfhead)==30);
|
||||
static_assert(sizeof(zip::centdirrec)==46);
|
||||
static_assert(sizeof(zip::endofcdr)==22);
|
||||
|
||||
for (size_t commentlen=0;commentlen<65536;commentlen++)
|
||||
{
|
||||
if (data.size() < sizeof(endofcdr)+commentlen) return NULL;
|
||||
|
||||
size_t trystart = data.size()-sizeof(endofcdr)-commentlen;
|
||||
endofcdr* ret = (endofcdr*)data.slice(trystart, sizeof(endofcdr)).ptr();
|
||||
if (ret->signature == ret->signature_expected)
|
||||
{
|
||||
if (ret->diskid_this != 0) return NULL;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
zip::centdirrec* zip::getcdr(arrayview<byte> data, endofcdr* end)
|
||||
{
|
||||
if (end->cdrstart_fromdisk+sizeof(centdirrec) > data.size()) return NULL;
|
||||
centdirrec* ret = (centdirrec*)data.slice(end->cdrstart_fromdisk, sizeof(centdirrec)).ptr();
|
||||
if (ret->signature != ret->signature_expected) return NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
zip::centdirrec* zip::nextcdr(arrayview<byte> data, centdirrec* cdr)
|
||||
{
|
||||
size_t start = (uint8_t*)cdr - data.ptr();
|
||||
size_t len = sizeof(centdirrec) + cdr->len_fname + cdr->len_exfield + cdr->len_fcomment;
|
||||
if (start+len+sizeof(centdirrec) > data.size()) return NULL;
|
||||
|
||||
centdirrec* next = (centdirrec*)data.slice(start+len, sizeof(centdirrec)).ptr();
|
||||
if (next->signature != next->signature_expected) return NULL;
|
||||
return next;
|
||||
}
|
||||
|
||||
zip::locfhead* zip::geth(arrayview<byte> data, centdirrec* cdr)
|
||||
{
|
||||
if (cdr->header_start+sizeof(locfhead) > data.size()) return NULL;
|
||||
locfhead* ret = (locfhead*)data.slice(cdr->header_start, sizeof(locfhead)).ptr();
|
||||
if (ret->signature != ret->signature_expected) return NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
arrayview<byte> zip::fh_fname(arrayview<byte> data, locfhead* fh)
|
||||
{
|
||||
size_t start = (uint8_t*)fh - data.ptr();
|
||||
if (start + sizeof(locfhead) + fh->len_fname > data.size()) return NULL;
|
||||
|
||||
return data.slice(start+sizeof(locfhead), fh->len_fname);
|
||||
}
|
||||
|
||||
arrayview<byte> zip::fh_data(arrayview<byte> data, locfhead* fh)
|
||||
{
|
||||
size_t start = (uint8_t*)fh - data.ptr();
|
||||
size_t len = sizeof(locfhead) + fh->len_fname + fh->len_exfield;
|
||||
if (start+len+sizeof(locfhead) > data.size()) return NULL;
|
||||
|
||||
return data.slice(start+len, fh->size_comp);
|
||||
}
|
||||
|
||||
bool zip::init(arrayview<byte> data)
|
||||
{
|
||||
filenames.reset();
|
||||
filedat.reset();
|
||||
|
||||
endofcdr* eod = getendofcdr(data);
|
||||
if (!eod) return false;
|
||||
|
||||
centdirrec* cdr = getcdr(data, eod);
|
||||
while (cdr)
|
||||
{
|
||||
locfhead* fh = geth(data, cdr);
|
||||
if (!fh) return false;
|
||||
|
||||
filenames.append(fromcp437(fh_fname(data, fh)));
|
||||
// some Apple zipper keeps zeroing half the fields in fh
|
||||
// and its Deflate is broken as well, tinfl returns failure
|
||||
file f = { fh->size_decomp, fh->compmethod, fh_data(data, fh), fh->crc32, fh->moddate };
|
||||
filedat.append(f);
|
||||
|
||||
cdr = nextcdr(data, cdr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////Invalidated whenever the file list changes.
|
||||
//arrayview<string> files()
|
||||
//{
|
||||
// return filenames;
|
||||
//}
|
||||
|
||||
size_t zip::find_file(cstring name)
|
||||
{
|
||||
for (size_t i=0;i<filenames.size();i++)
|
||||
{
|
||||
if (filenames[i]==name) return i;
|
||||
}
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
array<byte> zip::unpackfiledat(file& f)
|
||||
{
|
||||
switch (f.method)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
return f.data;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
array<byte> ret;
|
||||
ret.resize(f.decomplen);
|
||||
size_t actualsize = tinfl_decompress_mem_to_mem(ret.ptr(), ret.size(), f.data.ptr(), f.data.size(), 0);
|
||||
if (actualsize != f.decomplen) return NULL;
|
||||
return ret;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
array<byte> zip::read(cstring name, time_t * time)
|
||||
{
|
||||
size_t i = find_file(name);
|
||||
if (i==(size_t)-1) return NULL;
|
||||
|
||||
file& f = filedat[i];
|
||||
array<byte> ret = unpackfiledat(f);
|
||||
|
||||
//APPNOTE.TXT specifies some bizarre generator constant, 0xdebb20e3
|
||||
//no idea how to use that, the normal crc32 (0xedb88320) works fine
|
||||
if (crc32(ret) != f.crc32) return NULL;
|
||||
if (time) *time = fromdosdate(f.dosdate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void zip::write(cstring name, arrayview<byte> data, time_t date)
|
||||
{
|
||||
size_t i = find_file(name);
|
||||
if (!data)
|
||||
{
|
||||
if (i==(size_t)-1) return;
|
||||
else
|
||||
{
|
||||
filenames.remove(i);
|
||||
filedat.remove(i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (i==(size_t)-1)
|
||||
{
|
||||
i = filenames.size();
|
||||
filenames.append(name);
|
||||
}
|
||||
file& f = filedat[i];
|
||||
f.decomplen = data.size();
|
||||
f.crc32 = crc32(data);
|
||||
if (date) f.dosdate = todosdate(date); // else leave unchanged, or leave as 0
|
||||
|
||||
array<byte> comp;
|
||||
comp.resize(data.size());
|
||||
size_t complen = tdefl_compress_mem_to_mem(comp.ptr(), comp.size(), data.ptr(), data.size(), TDEFL_DEFAULT_MAX_PROBES);
|
||||
if (complen != 0 && complen < data.size())
|
||||
{
|
||||
comp.resize(complen);
|
||||
f.method = 8;
|
||||
f.data = std::move(comp);
|
||||
}
|
||||
else
|
||||
{
|
||||
f.method = 0;
|
||||
f.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
int zip::fileminver(zip::file& f)
|
||||
{
|
||||
if (f.method == 8) return 20;
|
||||
return 10;
|
||||
}
|
||||
|
||||
bool zip::strascii(cstring s)
|
||||
{
|
||||
for (size_t i=0;i<s.length();i++)
|
||||
{
|
||||
if (s[i]>=128 || s[i]<0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
array<byte> zip::pack()
|
||||
{
|
||||
array<byte> ret;
|
||||
|
||||
array<size_t> headerstarts;
|
||||
for (size_t i=0;i<filenames.size();i++)
|
||||
{
|
||||
headerstarts.append(ret.size());
|
||||
|
||||
file& f = filedat[i];
|
||||
locfhead h = {
|
||||
/*signature*/ locfhead::signature_expected,
|
||||
/*vermin*/ fileminver(f), // also contains host OS, but not important. and even if it was, pointless field
|
||||
/*bitflags*/ strascii(filenames[i]) ? 0 : 1<<11, // UTF-8 filenames
|
||||
/*compmethod*/ f.method,
|
||||
/*modtime*/ //merged
|
||||
/*moddate*/ f.dosdate,
|
||||
/*crc32*/ f.crc32,
|
||||
/*size_comp*/ f.data.size(),
|
||||
/*size_decomp*/ f.decomplen,
|
||||
/*len_fname*/ filenames[i].length(),
|
||||
/*len_exfield*/ 0,
|
||||
};
|
||||
arrayview<byte> hb((uint8_t*)&h, sizeof(h));
|
||||
ret += hb;
|
||||
ret += filenames[i].bytes();
|
||||
ret += f.data;
|
||||
}
|
||||
|
||||
size_t cdrstart = ret.size();
|
||||
for (size_t i=0;i<filenames.size();i++)
|
||||
{
|
||||
file& f = filedat[i];
|
||||
centdirrec cdr = {
|
||||
/*signature*/ centdirrec::signature_expected,
|
||||
/*verused*/ 63, // don't think anything really cares about this, just use latest
|
||||
/*vermin*/ fileminver(f),
|
||||
/*bitflags*/ strascii(filenames[i]) ? 0 : 1<<11,
|
||||
/*compmethod*/ f.method,
|
||||
/*modtime*/ //merged
|
||||
/*moddate*/ f.dosdate,
|
||||
/*crc32*/ f.crc32,
|
||||
/*size_comp*/ f.data.size(),
|
||||
/*size_decomp*/ f.decomplen,
|
||||
/*len_fname*/ filenames[i].length(),
|
||||
/*len_exfield*/ 0,
|
||||
/*len_fcomment*/ 0,
|
||||
/*disknr*/ 0,
|
||||
/*attr_int*/ 0,
|
||||
/*attr_ext*/ 0, // APPNOTE.TXT doesn't document this, other packers I checked are huge mazes. just gonna ignore it
|
||||
/*header_start*/ headerstarts[i],
|
||||
};
|
||||
arrayview<byte> cdrb((uint8_t*)&cdr, sizeof(cdr));
|
||||
ret += cdrb;
|
||||
ret += filenames[i].bytes();
|
||||
}
|
||||
size_t cdrend = ret.size();
|
||||
|
||||
endofcdr eod = {
|
||||
/*signature*/ endofcdr::signature_expected,
|
||||
/*diskid_this*/ 0,
|
||||
/*diskid_cdrst*/ 0,
|
||||
/*numfiles_thisdisk*/ filenames.size(),
|
||||
/*numfiles*/ filenames.size(),
|
||||
/*cdrsize*/ cdrend-cdrstart,
|
||||
/*cdrstart_fromdisk*/ cdrstart,
|
||||
/*zipcommentlen*/ 0,
|
||||
};
|
||||
arrayview<byte> eodb((uint8_t*)&eod, sizeof(eod));
|
||||
ret += eodb;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ARLIB_TEST
|
||||
const uint8_t zipbytes[433] = {
|
||||
0x50,0x4B,0x03,0x04,0x0A,0x03,0x00,0x00,0x00,0x00,0x2B,0xA5,0x8A,0x49,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x65,0x6D,
|
||||
0x70,0x74,0x79,0x2E,0x74,0x78,0x74,0x50,0x4B,0x03,0x04,0x0A,0x03,0x00,0x00,0x00,
|
||||
0x00,0x25,0xA5,0x8A,0x49,0x85,0x11,0x4A,0x0D,0x0B,0x00,0x00,0x00,0x0B,0x00,0x00,
|
||||
0x00,0x09,0x00,0x00,0x00,0x68,0x65,0x6C,0x6C,0x6F,0x2E,0x74,0x78,0x74,0x68,0x65,
|
||||
0x6C,0x6C,0x6F,0x20,0x77,0x6F,0x72,0x6C,0x64,0x50,0x4B,0x03,0x04,0x14,0x03,0x00,
|
||||
0x00,0x08,0x00,0x30,0xA5,0x8A,0x49,0x80,0x7B,0x90,0xA9,0x76,0x00,0x00,0x00,0xDD,
|
||||
0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x69,0x6E,0x6E,0x65,0x72,0x2E,0x7A,0x69,0x70,
|
||||
0x75,0x8D,0x3D,0x0E,0x40,0x40,0x10,0x85,0x97,0x45,0x82,0x28,0xDC,0x40,0x23,0x0A,
|
||||
0x89,0x2B,0xA8,0xD1,0x68,0xF5,0x24,0x8A,0xF5,0x13,0xD9,0x04,0x1D,0x85,0xC6,0x39,
|
||||
0xB8,0xA7,0xC1,0x6E,0x6C,0xB2,0xF1,0x26,0x93,0x99,0x64,0xBE,0x37,0x2F,0xCF,0xB0,
|
||||
0x66,0x61,0x04,0x0A,0xCF,0x3D,0x41,0x82,0x4C,0xE8,0xAA,0xE9,0xE9,0x1C,0xD1,0x89,
|
||||
0x7E,0x98,0x0F,0xD8,0xE6,0xA6,0x8E,0x0D,0xBB,0xCD,0xB0,0xBA,0x22,0xA4,0xBB,0xB1,
|
||||
0x67,0xF1,0xC6,0x6E,0x20,0x65,0x9E,0x29,0x6A,0x8C,0xFF,0x5E,0x73,0x79,0xCB,0xB1,
|
||||
0x22,0x31,0x88,0xD9,0xE4,0x28,0xC9,0x16,0xF0,0xE0,0xD7,0xA6,0x1B,0xF7,0x41,0x85,
|
||||
0x6A,0x61,0x16,0x0F,0x76,0x01,0x50,0x4B,0x01,0x02,0x3F,0x03,0x0A,0x03,0x00,0x00,
|
||||
0x00,0x00,0x2B,0xA5,0x8A,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x80,0xA4,0x81,
|
||||
0x00,0x00,0x00,0x00,0x65,0x6D,0x70,0x74,0x79,0x2E,0x74,0x78,0x74,0x50,0x4B,0x01,
|
||||
0x02,0x3F,0x03,0x0A,0x03,0x00,0x00,0x00,0x00,0x25,0xA5,0x8A,0x49,0x85,0x11,0x4A,
|
||||
0x0D,0x0B,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x20,0x80,0xA4,0x81,0x27,0x00,0x00,0x00,0x68,0x65,0x6C,0x6C,0x6F,
|
||||
0x2E,0x74,0x78,0x74,0x50,0x4B,0x01,0x02,0x3F,0x03,0x14,0x03,0x00,0x00,0x08,0x00,
|
||||
0x30,0xA5,0x8A,0x49,0x80,0x7B,0x90,0xA9,0x76,0x00,0x00,0x00,0xDD,0x00,0x00,0x00,
|
||||
0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x80,0xA4,0x81,0x59,0x00,
|
||||
0x00,0x00,0x69,0x6E,0x6E,0x65,0x72,0x2E,0x7A,0x69,0x70,0x50,0x4B,0x05,0x06,0x00,
|
||||
0x00,0x00,0x00,0x03,0x00,0x03,0x00,0xA5,0x00,0x00,0x00,0xF6,0x00,0x00,0x00,0x00,
|
||||
0x00
|
||||
};
|
||||
|
||||
template<typename T, typename U> bool member(arrayview<T> data, U item)
|
||||
{
|
||||
for (size_t i=0;i<data.size();i++)
|
||||
{
|
||||
if (data[i] == item) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
test("DOS timestamp conversion")
|
||||
{
|
||||
time_t unix = 1481402470; // 2016-12-10 20:41:10
|
||||
uint32_t dos = 0x498AA525;
|
||||
assert_eq(fromdosdate(dos), unix);
|
||||
assert_eq(todosdate(unix), dos);
|
||||
|
||||
//check 10000 random timestamps to ensure the conversion is lossless
|
||||
for (int i=0;i<10000;i++)
|
||||
{
|
||||
//pick a random unix timestamp in a suitable range
|
||||
//generation is in the unix domain, it's easier
|
||||
time_t unix_min = 631152000; // 1990-01-01 00:00:00
|
||||
time_t unix_max = 1577836800; // 2020-01-01 00:00:00
|
||||
time_t unix = unix_min + rand()%(unix_max-unix_min); // 100 million
|
||||
|
||||
time_t dos = todosdate(unix);
|
||||
assert_eq(fromdosdate(dos), unix&~1);
|
||||
}
|
||||
}
|
||||
|
||||
test("ZIP reading")
|
||||
{
|
||||
zip z;
|
||||
assert(z.init(arrayview<byte>(zipbytes, sizeof(zipbytes))));
|
||||
|
||||
arrayview<string> files = z.files();
|
||||
assert_eq(files.size(), 3);
|
||||
assert(member(files, "empty.txt"));
|
||||
assert(member(files, "hello.txt"));
|
||||
assert(member(files, "inner.zip"));
|
||||
|
||||
time_t t;
|
||||
assert_eq(z.read("empty.txt").size(), 0);
|
||||
assert_eq(string(z.read("hello.txt", &t)), "hello world");
|
||||
assert_eq(t, 1481402470);
|
||||
|
||||
zip z2;
|
||||
assert(z2.init(z.read("inner.zip")));
|
||||
|
||||
files = z2.files();
|
||||
assert_eq(files.size(), 2);
|
||||
assert(member(files, "empty.txt"));
|
||||
assert(member(files, "hello.txt"));
|
||||
|
||||
assert_eq(z2.read("empty.txt").size(), 0);
|
||||
assert_eq(string(z2.read("hello.txt")), "hello world");
|
||||
}
|
||||
|
||||
static arrayview<byte> sb(const char * str) { return arrayview<byte>((uint8_t*)str, strlen(str)); }
|
||||
test("ZIP writing")
|
||||
{
|
||||
zip z;
|
||||
assert(z.init(arrayview<byte>(zipbytes, sizeof(zipbytes))));
|
||||
|
||||
array<byte> zb = z.pack();
|
||||
assert_eq(zb.size(), sizeof(zipbytes));
|
||||
|
||||
//puts("");
|
||||
//puts("---------");
|
||||
//for (int i=0;i<zb.size();i++)
|
||||
//{
|
||||
// if (zb[i] == zipbytes[i]) printf("%.2X ",zb[i]);
|
||||
// else printf("(%.2X|%.2X) ",zipbytes[i],zb[i]);
|
||||
//}
|
||||
//puts("");
|
||||
//puts("---------");
|
||||
|
||||
z.write("hello.txt", sb("Hello World"));
|
||||
z.write("hello2.txt", sb("test"), 1000000000);
|
||||
z.write("empty.txt", sb(""));
|
||||
z.write("empty2.txt", sb(""));
|
||||
|
||||
zip z2;
|
||||
assert(z2.init(z.pack()));
|
||||
|
||||
arrayview<string> files = z2.files();
|
||||
assert_eq(files.size(), 3);
|
||||
assert(member(files, "hello2.txt"));
|
||||
assert(member(files, "hello.txt"));
|
||||
assert(member(files, "inner.zip"));
|
||||
|
||||
time_t t;
|
||||
assert_eq(string(z2.read("hello.txt", &t)), "Hello World");
|
||||
assert_eq(t, 1481402470); // if timestamp isn't set in the call, don't update it
|
||||
assert_eq(string(z2.read("hello2.txt", &t)), "test");
|
||||
assert_eq(t, 1000000000);
|
||||
|
||||
zip z3; // no initing
|
||||
array<byte> nuls;
|
||||
nuls.resize(65536);
|
||||
z3.write("nul.bin", nuls);
|
||||
|
||||
array<byte> nulsc = z3.pack();
|
||||
assert(nulsc.size() < 1024);
|
||||
zip z4;
|
||||
assert(z4.init(nulsc));
|
||||
array<byte> nulsdc = z4.read("nul.bin");
|
||||
for (int i=0;i<65536;i++) assert_eq(nulsdc[i], 0);
|
||||
}
|
||||
#endif
|
||||
58
arlib/zip.h
Normal file
58
arlib/zip.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
#include "array.h"
|
||||
#include "file.h"
|
||||
|
||||
class zip : nocopy {
|
||||
struct locfhead;
|
||||
struct centdirrec;
|
||||
struct endofcdr;
|
||||
|
||||
endofcdr* getendofcdr(arrayview<byte> data);
|
||||
centdirrec* getcdr(arrayview<byte> data, endofcdr* end);
|
||||
centdirrec* nextcdr(arrayview<byte> data, centdirrec* cdr);
|
||||
locfhead* geth(arrayview<byte> data, centdirrec* cdr);
|
||||
arrayview<byte> fh_fname(arrayview<byte> data, locfhead* fh);
|
||||
arrayview<byte> fh_data(arrayview<byte> data, locfhead* fh);
|
||||
|
||||
array<string> filenames;
|
||||
struct file {
|
||||
//would've put filenames here too, but then I'd need funky tricks in files()
|
||||
uint32_t decomplen;
|
||||
uint16_t method;
|
||||
array<byte> data;
|
||||
uint32_t crc32;
|
||||
uint32_t dosdate;
|
||||
};
|
||||
array<file> filedat;
|
||||
|
||||
public:
|
||||
zip() {}
|
||||
zip(arrayview<byte> data)
|
||||
{
|
||||
init(data);
|
||||
}
|
||||
|
||||
bool init(arrayview<byte> data);
|
||||
|
||||
//Invalidated whenever the file list changes.
|
||||
arrayview<string> files()
|
||||
{
|
||||
return filenames;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t find_file(cstring name);
|
||||
static array<byte> unpackfiledat(file& f);
|
||||
public:
|
||||
|
||||
array<byte> read(cstring name, time_t * time = NULL);
|
||||
|
||||
//Writing a blank array deletes the file.
|
||||
void write(cstring name, arrayview<byte> data, time_t date = 0);
|
||||
|
||||
private:
|
||||
static int fileminver(file& f);
|
||||
static bool strascii(cstring s);
|
||||
public:
|
||||
array<byte> pack();
|
||||
};
|
||||
Reference in New Issue
Block a user