*.gz
*.img
*.vmdk
+*.iso
SrcDoc/
APIDoc/
Usermode/*/*/UTEST/stage
#env:
# - ARCH=host HOST_ARCH=x86 USE_ACPICA=0
# - ARCH=host HOST_ARCH=x86_64 CC="$CC -m64"
-script: "make utest mtest"
+script:
+ - "make utest-build mtest-build"
+ - "make -k utest-run mtest-run"
ARM_CPUNAME = gerneric-armv7
CC = arm-eabi-gcc -mcpu=$(ARM_CPUNAME)
AS = arm-eabi-gcc -mcpu=$(ARM_CPUNAME) -c
+CXX = arm-eabi-g++ -mcpu=$(ARM_CPUNAME)
LD = arm-eabi-ld
OBJDUMP = arm-eabi-objdump
DISASM := $(OBJDUMP) -d -S
# Acess2 Build Configuration
#
-CC = i586-elf-gcc
-CXX = i586-elf-g++
+CC = i686-elf-gcc
+CXX = i686-elf-g++
#CC = clang -m32
-LD = i586-elf-ld
+LD = i686-elf-ld
#CC = gcc
#LD = ld
AS = nasm
-#OBJDUMP = i586-elf-objdump
+#OBJDUMP = i686-elf-objdump
OBJDUMP = objdump
RM = @rm -f
STRIP = strip
PATCHED_FILES := $(addprefix $(DIR)/,$(PATCHES))
-_patch: $(DIR) $(PATCHED_FILES) $(wildcard patches/UNIFIED.patch)
ifneq ($(wildcard patches/UNIFIED.patch),)
+$(DIR)/_unified_applied: $(wildcard patches/UNIFIED.patch)
cd $(DIR) && patch -p1 < ../patches/UNIFIED.patch
+ touch $@
+UNIFIED_TARGET=$(DIR)/_unified_applied
+else
+UNIFIED_TARGET=
endif
+
+_patch: $(DIR) $(PATCHED_FILES) $(UNIFIED_TARGET)
+
--- /dev/null
+#
+# Acess2 External: libcurl
+# - Makefile and patches by John Hodge (thePowersGang)
+#
+
+DEPS :=
+TARBALL_PATTERN := curl-*.tar.bz2
+TARBALL_TO_DIR_L := %.tar.bz2
+TARBALL_TO_DIR_R := %
+PATCHES := config.sub
+CONFIGURE_ARGS = LIBS=-lpsocket
+
+include ../common_automake.mk
+
--- /dev/null
+--- bochs-2.6.2_orig/config.sub 2013-06-17 11:39:39.670720710 +0800
++++ bochs-2.6.2/config.sub 2013-06-17 11:48:09.149384231 +0800
+@@ -1356,2 +1356,3 @@
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
++ | -acess2 \
+ | -aos* | -aros* \
+
include ../core.mk
-_build:
- cd $(BDIR) && CC=$(HOST)-gcc TARGET=sdl make
+.PHONY: _check_local_deps
+
+_build: _check_local_deps
+ cd $(BDIR) && CC=$(HOST)-gcc TARGET=framebuffer make
+
+_check_local_deps:
+ @gperf --help >/dev/null || (echo "ERROR: netsurf's build system requires gperf"; false)
{
if (vector == NULL || vector->current_item < 0)
return NULL;
+diff -ru .orig/netsurf-full-3.0//src/libnsfb-0.1.2/Makefile netsurf-full-3.0//src/libnsfb-0.1.0/Makefile
+--- .orig/netsurf-full-3.0//src/libnsfb-0.1.0/Makefile 2013-04-20 02:06:57.000000000 +0800
++++ netsurf-full-3.0//src/libnsfb-0.1.0/Makefile 2013-07-01 22:22:45.246689992 +0800
+@@ -31,5 +31,5 @@
+ # surfaces not detectable via pkg-config
+ NSFB_ABLE_AVAILABLE := no
+-NSFB_LINUX_AVAILABLE := yes
++NSFB_LINUX_AVAILABLE := no
+
+ # Flags and setup for each support library
+ NSFB_ABLE_AVAILABLE := no
+
// > If the `N` value is greater than `D`, we can set this bit
#define DEF_DIVMOD(s) Uint##s __divmod##s(Uint##s N, Uint##s D, Uint##s*Rem){\
Uint##s ret=0,add=1;\
- while(N>=D&&add) {D<<=1;add<<=1;}\
- while(add>1){\
- add>>=1;D>>=1;\
+ while(N/2>=D&&add) {D<<=1;add<<=1;}\
+ while(add>0){\
if(N>=D){ret+=add;N-=D;}\
+ add>>=1;D>>=1;\
}\
if(Rem)*Rem = N;\
return ret;\
const tPAddr MASK = ~CHANGEABLE_BITS; // Physical address and access bits
tVAddr rangeStart = 0;
tPAddr expected = CHANGEABLE_BITS; // CHANGEABLE_BITS is used because it's not a vaild value
- tVAddr curPos;
- Uint page;
tPAddr expected_pml4 = PF_WRITE|PF_USER;
tPAddr expected_pdp = PF_WRITE|PF_USER;
tPAddr expected_pd = PF_WRITE|PF_USER;
End &= (1L << 48) - 1;
- Start >>= 12; End >>= 12;
+ Start >>= 12;
+ End >>= 12;
- for(page = Start, curPos = Start<<12;
- page < End;
- curPos += 0x1000, page++)
+ // `page` will not overflow, End is 48-12 bits
+ tVAddr curPos = Start << 12;
+ for(Uint page = Start; page <= End; curPos += 0x1000, page++)
{
//Debug("&PAGEMAPLVL4(%i page>>27) = %p", page>>27, &PAGEMAPLVL4(page>>27));
//Debug("&PAGEDIRPTR(%i page>>18) = %p", page>>18, &PAGEDIRPTR(page>>18));
*ent = PAddr | 3;
- if( (tVAddr)VAddr < USER_MAX )
+ if( (tVAddr)VAddr <= USER_MAX )
*ent |= PF_USER;
INVLPG( VAddr );
.FindDir = SysFS_Comm_FindDir
};
tSysFS_Ent gSysFS_Version_Kernel = {
- NULL, NULL, // Nexts
- &gSysFS_Version, // Parent
- {
+ .Parent = &gSysFS_Version, // Parent
+ .Node = {
.Inode = 1, // File #1
.ImplPtr = NULL,
.ImplInt = (Uint)&gSysFS_Version_Kernel, // Self-Link
.ACLs = &gVFS_ACL_EveryoneRO,
.Type = &gSysFS_FileNodeType
},
- "Kernel"
+ .Name = {"Kernel"}
};
tSysFS_Ent gSysFS_Version = {
- NULL, NULL,
- &gSysFS_Root,
- {
+ .Parent = &gSysFS_Root,
+ .Node = {
.Size = 1,
.ImplPtr = &gSysFS_Version_Kernel,
.ImplInt = (Uint)&gSysFS_Version, // Self-Link
.Flags = VFS_FFLAG_DIRECTORY,
.Type = &gSysFS_DirNodeType
},
- "Version"
+ .Name = {"Version"}
};
// Root of the SysFS tree (just used to keep the code clean)
tSysFS_Ent gSysFS_Root = {
.ImplPtr = &gSysFS_Version,
.ImplInt = (Uint)&gSysFS_Root // Self-Link
},
- "/"
+ {"/"}
};
tDevFS_Driver gSysFS_DriverInfo = {
NULL, "system",
void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf)
{
int bytes_per_px = (Buf->Depth + 7) / 8;
- int y, save_pitch;
- Uint8 *dest, *src;
// Just a little sanity
if( !Buf->CursorBitmap || Buf->CursorX == -1 ) return ;
// Debug("DrvUtil_Video_RemoveCursor: (Buf=%p) dest_x=%i, dest_y=%i", Buf, Buf->CursorDestX, Buf->CursorDestY);
// Set up
- save_pitch = Buf->CursorBitmap->W * bytes_per_px;
- dest = (Uint8*)Buf->Framebuffer + Buf->CursorDestY * Buf->Pitch + Buf->CursorDestX*bytes_per_px;
- src = Buf->CursorSaveBuf;
-
+ size_t save_pitch = Buf->CursorBitmap->W * bytes_per_px;
+ Uint8 *dst = (Uint8*)Buf->Framebuffer + Buf->CursorDestY * Buf->Pitch + Buf->CursorDestX*bytes_per_px;
+ const Uint8 *src = Buf->CursorSaveBuf;
+
+ ASSERT(Buf->Framebuffer);
+ ASSERT(src);
+ ASSERT(CheckMem(dst, Buf->CursorRenderH*Buf->Pitch));
+ ASSERT(CheckMem(src, Buf->CursorRenderH*save_pitch));
+
// Copy each line back
- for( y = 0; y < Buf->CursorRenderH; y ++ )
+ for( int y = 0; y < Buf->CursorRenderH; y ++ )
{
- memcpy( dest, src, Buf->CursorRenderW * bytes_per_px );
+ memcpy( dst, src, Buf->CursorRenderW * bytes_per_px );
src += save_pitch;
- dest += Buf->Pitch;
+ dst += Buf->Pitch;
}
// Set the cursor as removed
*/
#define DEBUG 0
#include <acess.h>
-#include <threads_int.h>
#include <events.h>
+#include <threads_int.h>
// === CODE ===
void Threads_PostEvent(tThread *Thread, Uint32 EventMask)
// INVLPTR is returned from Heap_Allocate when the allocation
// size is zero.
if( Ptr == INVLPTR ) return;
+ // free(NULL) is a no-op
+ if( Ptr == NULL ) return;
// Alignment Check
if( (tVAddr)Ptr % sizeof(void*) != 0 ) {
* \{
* \todo Move to mm_virt.h
*/
-#define MM_PFLAG_RO 0x01 // Writes disallowed
-#define MM_PFLAG_EXEC 0x02 // Allow execution
-#define MM_PFLAG_NOPAGE 0x04 // Prevent from being paged out
-#define MM_PFLAG_COW 0x08 // Copy-On-Write
-#define MM_PFLAG_KERNEL 0x10 // Kernel-Only (Ring0)
+#define MM_PFLAG_RO 0x01 //!< Writes disallowed
+#define MM_PFLAG_EXEC 0x02 //!< Allow execution
+#define MM_PFLAG_NOPAGE 0x04 //!< Prevent from being paged out
+#define MM_PFLAG_COW 0x08 //!< Copy-On-Write
+#define MM_PFLAG_KERNEL 0x10 //!< Kernel-Only (Ring0)
/**
* \}
*/
* \name IRQ hander registration
* \{
*/
+//! Register a callback for when an IRQ is raised
extern int IRQ_AddHandler(int Num, void (*Callback)(int, void*), void *Ptr);
+//! Remove a previously registered IRQ handler
extern void IRQ_RemHandler(int Handle);
/**
* \}
#include <stdarg.h>
/**
- * \name Strings
+ * \name String Manipulation
* \{
*/
// - stdio.h in userland
extern signed long long strtoll(const char *str, char **end, int base);
extern signed long strtol(const char *str, char **end, int base);
+//! \brief String comparison (case-insensitive)
extern int strucmp(const char *Str1, const char *Str2);
extern int strpos(const char *Str, char Ch);
extern int strpos8(const char *str, Uint32 search);
* user.
* - Drivers for specific types of hardware must behave in the specific
* way described here.
+ * - \ref library "Library Functions"
+ * - Kernel's version of libc and other helper functions
*
* \page drivers Device Drivers
*
extern Uint32 PCI_GetBAR(tPCIDev id, int BAR);\r
extern Uint64 PCI_GetValidBAR(tPCIDev id, int BAR, tPCI_BARType BARType);\r
//extern Uint16 PCI_AssignPort(tPCIDev id, int bar, int count);\r
+//extern void* PCI_MapMemBAR(tPCIDev id, int BAR, tPCI_BARType BARType, size_t* Size, tPAddr* PAddr);\r
\r
#endif\r
// === CODE ===
// - Import userland stroi.c file
#define _LIB_H_
+#define _SysDebug(f,v...) Log_Debug("libc", f ,## v)
#include "../../Usermode/Libraries/libc.so_src/strtoi.c"
int ParseInt(const char *string, int *Val)
*/
void Threads_DumpActive(void)
{
- tThread *thread;
- tThreadList *list;
- #if SCHEDULER_TYPE == SCHED_RR_PRI
- int i;
- #endif
-
Log("Active Threads: (%i reported)", giNumActiveThreads);
#if SCHEDULER_TYPE == SCHED_RR_PRI
- for( i = 0; i < MIN_PRIORITY+1; i++ )
+ for( int i = 0; i < MIN_PRIORITY+1; i++ )
{
- list = &gaActiveThreads[i];
+ tThreadList *list = &gaActiveThreads[i];
#else
- list = &gActiveThreads;
+ tThreadList *list = &gActiveThreads;
#endif
- for(thread=list->Head;thread;thread=thread->Next)
+ for(tThread *thread = list->Head; thread; thread = thread->Next)
{
Threads_int_DumpThread(thread);
if(thread->Status != THREAD_STAT_ACTIVE)
dev = PCI_GetDevice(0x1234, 0x1111, 0);\r
if(dev == -1)\r
base = VBE_DISPI_LFB_PHYSICAL_ADDRESS;\r
- else\r
- base = PCI_GetBAR(dev, 0);\r
+ else {\r
+ Log_Debug("BGA", "BARs %x,%x,%x,%x,%x,%x",\r
+ PCI_GetBAR(dev, 0), PCI_GetBAR(dev, 1), PCI_GetBAR(dev, 2),\r
+ PCI_GetBAR(dev, 3), PCI_GetBAR(dev, 4), PCI_GetBAR(dev, 5));\r
+ base = PCI_GetValidBAR(dev, 0, PCI_BARTYPE_MEM);\r
+ // TODO: Qemu/bochs have MMIO versions of the registers in BAR2\r
+ // - This range is non-indexed\r
+ //mmio_base = PCI_GetValidBAR(dev, 2, PCI_BARTYPE_MEM);\r
+ }\r
\r
// Map Framebuffer to hardware address\r
gBGA_Framebuffer = (void *) MM_MapHWPages(base, 768); // 768 pages (3Mb)\r
switch(ID)
{
- BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaTegra2Vid_IOCtls);
+ BASE_IOCTLS(DRV_TYPE_VIDEO, "Tegra2", VERSION, csaTegra2Vid_IOCtls);
case VIDEO_IOCTL_SETBUFFORMAT:
DrvUtil_Video_RemoveCursor( &gTegra2Vid_DrvUtil_BufInfo );
#include <limits.h>\r
\r
// === CONSTANTS ===\r
-#define USE_BIOS 1\r
+#ifdef ARCHDIR_is_x86\r
+# define USE_BIOS 1\r
+#else\r
+# define USE_BIOS 0\r
+#endif\r
#define VESA_DEFAULT_FRAMEBUFFER (KERNEL_BASE|0xA0000)\r
#define BLINKING_CURSOR 0\r
#if BLINKING_CURSOR\r
int Vesa_Int_ModeInfo(tVideo_IOCtl_Mode *data);\r
void Vesa_int_HideCursor(void);\r
void Vesa_int_ShowCursor(void);\r
+ int Vesa_int_SetCursor(tVideo_IOCtl_Bitmap *Cursor);\r
void Vesa_FlipCursor(void *Arg);\r
Uint16 VBE_int_GetWord(const tVT_Char *Char);\r
void VBE_int_Text_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour);\r
\r
// Allocate Info Block\r
info = VM8086_Allocate(gpVesa_BiosState, 512, &infoPtr.seg, &infoPtr.ofs);\r
+ if( info == NULL ) {\r
+ Log_Warning("VBE", "VM8086 allocation error");\r
+ return MODULE_ERR_NOTNEEDED;\r
+ }\r
// Set Requested Version\r
memcpy(info->signature, "VBE2", 4);\r
// Set Registers\r
case VIDEO_IOCTL_SETBUFFORMAT:\r
Vesa_int_HideCursor();\r
ret = gVesa_BufInfo.BufferFormat;\r
- if(Data) gVesa_BufInfo.BufferFormat = *(int*)Data;\r
+ if(Data)\r
+ gVesa_BufInfo.BufferFormat = *(int*)Data;\r
if(gVesa_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
- DrvUtil_Video_SetCursor( &gVesa_BufInfo, &gDrvUtil_TextModeCursor );\r
+ Vesa_int_SetCursor(&gDrvUtil_TextModeCursor);\r
Vesa_int_ShowCursor();\r
return ret;\r
\r
case VIDEO_IOCTL_SETCURSOR: // Set cursor position\r
+ if( !CheckMem(Data, sizeof(tVideo_IOCtl_Pos)) )\r
+ return -EINVAL;\r
Vesa_int_HideCursor();\r
giVesaCursorX = ((tVideo_IOCtl_Pos*)Data)->x;\r
giVesaCursorY = ((tVideo_IOCtl_Pos*)Data)->y;\r
return 0;\r
\r
case VIDEO_IOCTL_SETCURSORBITMAP:\r
- if( gpVesaCurMode->flags & FLAG_LFB )\r
- DrvUtil_Video_SetCursor( &gVesa_BufInfo, Data );\r
- return 0;\r
+ return Vesa_int_SetCursor(Data);\r
}\r
return 0;\r
}\r
}\r
}\r
\r
+int Vesa_int_SetCursor(tVideo_IOCtl_Bitmap *Cursor)\r
+{\r
+ if( !CheckMem(Cursor, sizeof(tVideo_IOCtl_Bitmap)) )\r
+ return -EINVAL;\r
+ \r
+ if( gpVesaCurMode && gpVesaCurMode->flags & FLAG_LFB )\r
+ {\r
+ DrvUtil_Video_SetCursor( &gVesa_BufInfo, Cursor );\r
+ }\r
+ else\r
+ {\r
+ }\r
+ return 0;\r
+}\r
+\r
/**\r
* \brief Swaps the text cursor on/off\r
*/\r
Uint16 RouterLifetime; // Seconds, life time as a default router (wtf does that mean?)
Uint32 ReachableTime;
Uint32 RetransTimer; // Miliseconds, time between transmissions of RAs from this router
- Uint8 Options[];
+ Uint8 Options[0];
} PACKED tICMPv6_RA;
typedef struct {
Uint32 Reserved;
tIPv6 TargetAddress;
- Uint8 Options[];
+ Uint8 Options[0];
} PACKED tICMPv6_NS;
typedef struct {
Uint32 Flags;
tIPv6 TargetAddress;
- Uint8 Options[];
+ Uint8 Options[0];
} PACKED tICMPv6_NA;
typedef struct {
Uint32 Reserved;
tIPv6 TargetAddress;
tIPv6 DestinationAddress;
- Uint8 Options[];
+ Uint8 Options[0];
} PACKED tICMPv6_Redirect;
typedef struct {
Uint8 Type; // 1,2
Uint8 Length; // Length of field in units of 8 bytes (incl header), typically 1
- Uint8 Address[];
+ Uint8 Address[0];
} PACKED tICMPv6_Opt_LinkAddr;
typedef struct {
Uint32 ValidLifetime;
Uint32 PreferredLifetime;
Uint32 _reserved2;
- tIPv6 Prefix[];
+ tIPv6 Prefix[0];
} PACKED tICMPv6_Opt_Prefix;
typedef struct {
Uint8 Length;
Uint16 _rsvd1;
Uint32 _rsvd2;
- Uint8 Data[]; // All or part of the redirected message (not exceeding MTU)
+ Uint8 Data[0]; // All or part of the redirected message (not exceeding MTU)
} PACKED tICMPv6_Opt_Redirect;
typedef struct {
Uint8 HopLimit;
tIPv6 Source;
tIPv6 Destination;
- char Data[];
+ char Data[0];
};
#define IPV6_ETHERNET_ID 0x86DD
#define TCP_WINDOW_SIZE 0x2000
#define TCP_RECIEVE_BUFFER_SIZE 0x8000
#define TCP_DACK_THRESHOLD 4096
-#define TCP_DACK_TIMEOUT 500
+#define TCP_DACK_TIMEOUT 100
-#define TCP_DEBUG 0 // Set to non-0 to enable TCP packet logging
+#define TCP_DEBUG 1 // Set to non-0 to enable TCP packet logging
// === PROTOTYPES ===
void TCP_Initialise(void);
void TCP_SendPacket(tTCPConnection *Conn, tTCPHeader *Header, size_t DataLen, const void *Data);
void TCP_int_SendPacket(tInterface *Interface, const void *Dest, tTCPHeader *Header, size_t Length, const void *Data);
void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer);
-void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length);
+ int TCP_INT_HandleServerPacket(tInterface *Interface, tTCPListener *Server, const void *Address, tTCPHeader *Header, size_t Length);
+ int TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length);
int TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t Length);
void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection);
void TCP_int_SendDelayedACK(void *ConnPtr);
IPStack_Buffer_AppendSubBuffer(buffer, Length, 0, Data, NULL, NULL);
IPStack_Buffer_AppendSubBuffer(buffer, sizeof(*Header), 0, Header, NULL, NULL);
- LOG("Sending %i+%i to %s:%i", sizeof(*Header), Length,
+ #if TCP_DEBUG
+ Log_Log("TCP", "TCP_int_SendPacket: <Local>:%i to [%s]:%i (%i data), Flags = %s%s%s%s%s%s%s%s",
+ ntohs(Header->SourcePort),
IPStack_PrintAddress(Interface->Type, Dest),
- ntohs(Header->DestPort)
+ ntohs(Header->DestPort),
+ Length,
+ (Header->Flags & TCP_FLAG_CWR) ? "CWR " : "",
+ (Header->Flags & TCP_FLAG_ECE) ? "ECE " : "",
+ (Header->Flags & TCP_FLAG_URG) ? "URG " : "",
+ (Header->Flags & TCP_FLAG_ACK) ? "ACK " : "",
+ (Header->Flags & TCP_FLAG_PSH) ? "PSH " : "",
+ (Header->Flags & TCP_FLAG_RST) ? "RST " : "",
+ (Header->Flags & TCP_FLAG_SYN) ? "SYN " : "",
+ (Header->Flags & TCP_FLAG_FIN) ? "FIN " : ""
);
+ #endif
Header->Checksum = 0;
Header->Checksum = TCP_int_CalculateChecksum(Interface->Type, Interface->Address, Dest,
}
}
-void TCP_int_SendRSTTo(tInterface *Interface, void *Address, size_t Length, const tTCPHeader *Header)
+void TCP_int_SendRSTTo(tInterface *Interface, const void *Address, size_t Length, const tTCPHeader *Header)
{
tTCPHeader out_hdr = {0};
{
// Check that it is coming in on the same interface
if(conn->Interface != Interface) continue;
-
// Check Source Port
- Log_Log("TCP", "TCP_GetPacket: conn->RemotePort(%i) == hdr->SourcePort(%i)",
- conn->RemotePort, ntohs(hdr->SourcePort));
if(conn->RemotePort != ntohs(hdr->SourcePort)) continue;
-
// Check Source IP
- Log_Debug("TCP", "TCP_GetPacket: conn->RemoteIP(%s)",
- IPStack_PrintAddress(conn->Interface->Type, &conn->RemoteIP));
- Log_Debug("TCP", " == Address(%s)",
- IPStack_PrintAddress(conn->Interface->Type, Address));
if( IPStack_CompareAddress(conn->Interface->Type, &conn->RemoteIP, Address, -1) == 0 )
continue ;
Log_Log("TCP", "TCP_GetPacket: Matches connection %p", conn);
// We have a response!
- TCP_INT_HandleConnectionPacket(conn, hdr, Length);
-
- return;
+ if( TCP_INT_HandleConnectionPacket(conn, hdr, Length) == 0 )
+ return;
+ break ;
}
-
- if( hdr->Flags & TCP_FLAG_RST ) {
- LOG("RST, ignore");
- return ;
- }
- else if( hdr->Flags & TCP_FLAG_ACK ) {
- LOG("ACK, send RST");
- TCP_int_SendRSTTo(Interface, Address, Length, hdr);
- return ;
- }
- else if( !(hdr->Flags & TCP_FLAG_SYN) ) {
- LOG("Other, ignore");
- return ;
- }
- Log_Log("TCP", "TCP_GetPacket: Opening Connection");
-
- // TODO: Check for halfopen max
-
- tTCPConnection *conn = TCP_int_CreateConnection(Interface, TCP_ST_SYN_RCVD);
- conn->LocalPort = srv->Port;
- conn->RemotePort = ntohs(hdr->SourcePort);
-
- switch(Interface->Type)
- {
- case 4: conn->RemoteIP.v4 = *(tIPv4*)Address; break;
- case 6: conn->RemoteIP.v6 = *(tIPv6*)Address; break;
- default: ASSERTC(Interface->Type,==,4); return;
- }
-
- conn->NextSequenceRcv = ntohl( hdr->SequenceNumber ) + 1;
- conn->HighestSequenceRcvd = conn->NextSequenceRcv;
- conn->NextSequenceSend = rand();
- conn->LastACKSequence = ntohl( hdr->SequenceNumber );
-
- conn->Node.ImplInt = srv->NextID ++;
-
- // Hmm... Theoretically, this lock will never have to wait,
- // as the interface is locked to the watching thread, and this
- // runs in the watching thread. But, it's a good idea to have
- // it, just in case
- // Oh, wait, there is a case where a wildcard can be used
- // (srv->Interface == NULL) so having the lock is a good idea
- SHORTLOCK(&srv->lConnections);
- conn->Server = srv;
- conn->Prev = srv->ConnectionsTail;
- if(srv->Connections) {
- ASSERT(srv->ConnectionsTail);
- srv->ConnectionsTail->Next = conn;
- }
- else {
- ASSERT(!srv->ConnectionsTail);
- srv->Connections = conn;
- }
- srv->ConnectionsTail = conn;
- if(!srv->NewConnections)
- srv->NewConnections = conn;
- VFS_MarkAvaliable( &srv->Node, 1 );
- SHORTREL(&srv->lConnections);
- Semaphore_Signal(&srv->WaitingConnections, 1);
-
- // Send the SYN ACK
- hdr->Flags |= TCP_FLAG_ACK;
- hdr->AcknowlegementNumber = htonl(conn->NextSequenceRcv);
- hdr->SequenceNumber = htonl(conn->NextSequenceSend);
- hdr->DestPort = hdr->SourcePort;
- hdr->SourcePort = htons(srv->Port);
- hdr->DataOffset = (sizeof(tTCPHeader)/4) << 4;
- TCP_SendPacket( conn, hdr, 0, NULL );
- conn->NextSequenceSend ++;
+ TCP_INT_HandleServerPacket(Interface, srv, Address, hdr, Length);
return ;
}
if(conn->RemotePort != ntohs(hdr->SourcePort)) continue;
// Check Source IP
- if(conn->Interface->Type == 6 && !IP6_EQU(conn->RemoteIP.v6, *(tIPv6*)Address))
- continue;
- if(conn->Interface->Type == 4 && !IP4_EQU(conn->RemoteIP.v4, *(tIPv4*)Address))
- continue;
+ if( IPStack_CompareAddress(conn->Interface->Type, &conn->RemoteIP, Address, -1) == 0 )
+ continue ;
- TCP_INT_HandleConnectionPacket(conn, hdr, Length);
- return ;
+ // Handle or fall through
+ if( TCP_INT_HandleConnectionPacket(conn, hdr, Length) == 0 )
+ return ;
+ break;
}
}
}
}
+/*
+ * Handle packets in LISTEN state
+ */
+int TCP_INT_HandleServerPacket(tInterface *Interface, tTCPListener *Server, const void *Address, tTCPHeader *Header, size_t Length)
+{
+ if( Header->Flags & TCP_FLAG_RST ) {
+ LOG("RST, ignore");
+ return 0;
+ }
+ else if( Header->Flags & TCP_FLAG_ACK ) {
+ LOG("ACK, send RST");
+ TCP_int_SendRSTTo(Interface, Address, Length, Header);
+ return 0;
+ }
+ else if( !(Header->Flags & TCP_FLAG_SYN) ) {
+ LOG("Other, ignore");
+ return 0;
+ }
+
+ Log_Log("TCP", "TCP_GetPacket: Opening Connection");
+
+ // TODO: Check security (a TCP Option)
+ // TODO: Check SEG.PRC
+ // TODO: Check for halfopen max
+
+ tTCPConnection *conn = TCP_int_CreateConnection(Interface, TCP_ST_SYN_RCVD);
+ conn->LocalPort = Server->Port;
+ conn->RemotePort = ntohs(Header->SourcePort);
+
+ switch(Interface->Type)
+ {
+ case 4: conn->RemoteIP.v4 = *(tIPv4*)Address; break;
+ case 6: conn->RemoteIP.v6 = *(tIPv6*)Address; break;
+ default: ASSERTC(Interface->Type,==,4); return 0;
+ }
+
+ conn->NextSequenceRcv = ntohl( Header->SequenceNumber ) + 1;
+ conn->HighestSequenceRcvd = conn->NextSequenceRcv;
+ conn->NextSequenceSend = rand();
+ conn->LastACKSequence = ntohl( Header->SequenceNumber );
+
+ conn->Node.ImplInt = Server->NextID ++;
+
+ // Hmm... Theoretically, this lock will never have to wait,
+ // as the interface is locked to the watching thread, and this
+ // runs in the watching thread. But, it's a good idea to have
+ // it, just in case
+ // Oh, wait, there is a case where a wildcard can be used
+ // (Server->Interface == NULL) so having the lock is a good idea
+ SHORTLOCK(&Server->lConnections);
+ conn->Server = Server;
+ conn->Prev = Server->ConnectionsTail;
+ if(Server->Connections) {
+ ASSERT(Server->ConnectionsTail);
+ Server->ConnectionsTail->Next = conn;
+ }
+ else {
+ ASSERT(!Server->ConnectionsTail);
+ Server->Connections = conn;
+ }
+ Server->ConnectionsTail = conn;
+ if(!Server->NewConnections)
+ Server->NewConnections = conn;
+ VFS_MarkAvaliable( &Server->Node, 1 );
+ SHORTREL(&Server->lConnections);
+ Semaphore_Signal(&Server->WaitingConnections, 1);
+
+ // Send the SYN ACK
+ Header->Flags = TCP_FLAG_ACK|TCP_FLAG_SYN;
+ Header->AcknowlegementNumber = htonl(conn->NextSequenceRcv);
+ Header->SequenceNumber = htonl(conn->NextSequenceSend);
+ Header->DestPort = Header->SourcePort;
+ Header->SourcePort = htons(Server->Port);
+ Header->DataOffset = (sizeof(tTCPHeader)/4) << 4;
+ TCP_SendPacket( conn, Header, 0, NULL );
+ conn->NextSequenceSend ++;
+ return 0;
+}
+
/**
* \brief Handles a packet sent to a specific connection
* \param Connection TCP Connection pointer
* \param Header TCP Packet pointer
* \param Length Length of the packet
*/
-void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length)
+int TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length)
{
int dataLen;
Uint32 sequence_num;
// TODO: Check if this needs to be here
if( Connection->State == TCP_ST_FINISHED ) {
Log_Log("TCP", "Packet ignored - connection finnished");
- return ;
+ return 1;
+ }
+ if( Connection->State == TCP_ST_FORCE_CLOSE ) {
+ Log_Log("TCP", "Packet ignored - connection reset");
+ return 1;
}
// Syncronise sequence values
if( Header->Flags & TCP_FLAG_ACK )
{
Log_Log("TCP", "ACKing SYN-ACK");
- Connection->State = TCP_ST_OPEN;
+ Connection->State = TCP_ST_ESTABLISHED;
VFS_MarkFull(&Connection->Node, 0);
TCP_INT_SendACK(Connection, "SYN-ACK");
}
// SYN-ACK sent, expecting ACK
case TCP_ST_SYN_RCVD:
+ if( Header->Flags & TCP_FLAG_RST )
+ {
+ Log_Log("TCP", "RST Received, closing");
+ Connection->State = TCP_ST_FORCE_CLOSE;
+ VFS_MarkError(&Connection->Node, 1);
+ return 0;
+ }
if( Header->Flags & TCP_FLAG_ACK )
{
// TODO: Handle max half-open limit
Log_Log("TCP", "Connection fully opened");
- Connection->State = TCP_ST_OPEN;
+ Connection->State = TCP_ST_ESTABLISHED;
VFS_MarkFull(&Connection->Node, 0);
}
break;
// --- Established State ---
- case TCP_ST_OPEN:
+ case TCP_ST_ESTABLISHED:
// - Handle State changes
//
+ if( Header->Flags & TCP_FLAG_RST )
+ {
+ Log_Log("TCP", "Conn %p closed, received RST");
+ // Error outstanding transactions
+ Connection->State = TCP_ST_FORCE_CLOSE;
+ VFS_MarkError(&Connection->Node, 1);
+ return 0;
+ }
if( Header->Flags & TCP_FLAG_FIN ) {
Log_Log("TCP", "Conn %p closed, recieved FIN", Connection);
VFS_MarkError(&Connection->Node, 1);
TCP_INT_SendACK(Connection, "FIN Received");
Connection->State = TCP_ST_CLOSE_WAIT;
// CLOSE WAIT requires the client to close
- return ;
+ return 0;
}
// Check for an empty packet
if( Header->Flags == TCP_FLAG_ACK )
{
Log_Log("TCP", "ACK only packet");
- return ;
+ return 0;
}
// TODO: Is this right? (empty packet counts as one byte)
if( Connection->HighestSequenceRcvd == Connection->NextSequenceRcv )
Connection->NextSequenceRcv ++;
Log_Log("TCP", "Empty Packet, inc and ACK the current sequence number");
TCP_INT_SendACK(Connection, "Empty");
- return ;
+ return 0;
}
// NOTES:
Connection->State = TCP_ST_FIN_WAIT2;
Log_Debug("TCP", "Conn %p closed, sent FIN ACKed", Connection);
VFS_MarkError(&Connection->Node, 1);
- return ;
+ return 0;
}
break;
Connection->State = TCP_ST_TIME_WAIT;
Log_Debug("TCP", "Conn %p CLOSING -> TIME WAIT", Connection);
VFS_MarkError(&Connection->Node, 1);
- return ;
+ return 0;
}
break;
case TCP_ST_FINISHED:
Log_Log("TCP", "Packets when CLOSED, ignoring");
break;
+ case TCP_ST_FORCE_CLOSE:
+ Log_Log("TCP", "Packets when force CLOSED, ignoring");
+ return 1;
//default:
// Log_Warning("TCP", "Unhandled TCP state %i", Connection->State);
// break;
}
+ return 0;
+
}
/**
void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection)
{
// Calculate length of contiguous bytes
- const int length = Connection->HighestSequenceRcvd - Connection->NextSequenceRcv;
+ const size_t length = Connection->HighestSequenceRcvd - Connection->NextSequenceRcv;
Uint32 index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE;
size_t runlength = length;
- LOG("length=%i, index=0x%x", length, index);
+ LOG("HSR=0x%x,NSR=0x%x", Connection->HighestSequenceRcvd, Connection->NextSequenceRcv);
+ if( Connection->HighestSequenceRcvd == Connection->NextSequenceRcv )
+ {
+ return ;
+ }
+ LOG("length=%u, index=0x%x", length, index);
for( int i = 0; i < length; i ++ )
{
int bit = index % 8;
Semaphore_Init(conn->SentBufferSpace, 0, TCP_SEND_BUFFER_SIZE, "TCP SentBuffer", conn->Name);
#endif
+ conn->HighestSequenceRcvd = 0;
#if CACHE_FUTURE_PACKETS_IN_BYTES
// Future recieved data (ahead of the expected sequence number)
conn->FuturePacketData = (Uint8*)conn + sizeof(tTCPConnection);
conn->Server = NULL;
conn->Prev = NULL;
conn->Next = gTCP_OutbountCons;
- gTCP_OutbountCons->Prev = conn;
+ if(gTCP_OutbountCons)
+ gTCP_OutbountCons->Prev = conn;
gTCP_OutbountCons = conn;
SHORTREL(&glTCP_OutbountCons);
tTCPConnection *conn = Node->ImplPtr;
size_t len;
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+ ENTER("pNode XOffset xLength pBuffer", Node, Offset, Length, Buffer);
LOG("conn = %p {State:%i}", conn, conn->State);
// If the connection has been closed (state > ST_OPEN) then clear
// any stale data in the buffer (until it is empty (until it is empty))
- if( conn->State > TCP_ST_OPEN )
+ if( conn->State > TCP_ST_ESTABLISHED )
{
+ LOG("Connection closed");
Mutex_Acquire( &conn->lRecievedPackets );
len = RingBuffer_Read( Buffer, conn->RecievedBuffer, Length );
Mutex_Release( &conn->lRecievedPackets );
// #endif
// Don't allow a write to a closed connection
- if( conn->State > TCP_ST_OPEN ) {
+ if( conn->State > TCP_ST_ESTABLISHED ) {
VFS_MarkError(Node, 1);
errno = 0;
LEAVE('i', -1);
}
Node->ReferenceCount --;
- if( conn->State == TCP_ST_CLOSE_WAIT || conn->State == TCP_ST_OPEN )
+ if( conn->State == TCP_ST_CLOSE_WAIT || conn->State == TCP_ST_ESTABLISHED )
{
packet.SourcePort = htons(conn->LocalPort);
packet.DestPort = htons(conn->RemotePort);
Log_Warning("TCP", "Closing connection that was never opened");
TCP_int_FreeTCB(conn);
break;
+ case TCP_ST_FORCE_CLOSE:
+ conn->State = TCP_ST_FINISHED;
+ TCP_int_FreeTCB(conn);
+ break;
case TCP_ST_CLOSE_WAIT:
conn->State = TCP_ST_LAST_ACK;
break;
- case TCP_ST_OPEN:
+ case TCP_ST_ESTABLISHED:
conn->State = TCP_ST_FIN_WAIT1;
while( conn->State == TCP_ST_FIN_WAIT1 )
Threads_Yield();
TCP_ST_SYN_SENT, // 1 - SYN sent by local, waiting for SYN-ACK
TCP_ST_SYN_RCVD, // 2 - SYN recieved, SYN-ACK sent
- TCP_ST_OPEN, // 3 - Connection open
+ TCP_ST_ESTABLISHED, // 3 - Connection open
// Local Close
TCP_ST_FIN_WAIT1, // 4 - FIN sent, waiting for reply (ACK or FIN)
TCP_ST_CLOSING, // 6 - Waiting for ACK of FIN (FIN sent and recieved)
TCP_ST_TIME_WAIT, // 7 - Waiting for timeout after local close
// Remote close
- TCP_ST_CLOSE_WAIT, // 8 - FIN recieved, waiting for user to close (error set, wait for node close)
- TCP_ST_LAST_ACK, // 9 - FIN sent and recieved, waiting for ACK
- TCP_ST_FINISHED // 10 - Essentially closed, all packets are invalid
+ TCP_ST_FORCE_CLOSE, // 8 - RST recieved, waiting for user close
+ TCP_ST_CLOSE_WAIT, // 9 - FIN recieved, waiting for user to close (error set, wait for node close)
+ TCP_ST_LAST_ACK, // 10 - FIN sent and recieved, waiting for ACK
+ TCP_ST_FINISHED // 11 - Essentially closed, all packets are invalid
};
struct sTCPConnection
*/
tKeyboard *Keyboard_CreateInstance(int MaxSym, const char *Name)
{
- tKeyboard *ret;
- int sym_bitmap_size = (MaxSym + 7)/8;
- int string_size = strlen(Name) + 1;
+ size_t sym_bitmap_size = (MaxSym + 7)/8;
+ size_t string_size = strlen(Name) + 1;
- ret = malloc( sizeof(tKeyboard) + sym_bitmap_size + string_size );
+ tKeyboard *ret = calloc( 1, sizeof(tKeyboard) + sym_bitmap_size + string_size );
if( !ret ) {
return NULL;
}
- // Clear
- memset(ret, 0, sizeof(tKeyboard) + sym_bitmap_size );
// Set name
- ret->Name = (char*)ret + sizeof(tKeyboard) + sym_bitmap_size;
- memcpy(ret->Name, Name, string_size);
+ ret->Name = (char*)( &ret->KeyStates[sym_bitmap_size] );
+ strcpy(ret->Name, Name);
// Set node and default keymap
ret->Node = &gKB_DevInfo.RootNode;
ret->Keymap = &gKeymap_KBDUS;
#
#
-CPPFLAGS = -I../../../../UDI/include -Iinclude
+CPPFLAGS = -I../../../../UDI/include -Iinclude -D UDI_VERSION=0x101
# - UDI Library Files
LIB_OBJS := core/logging.o core/strmem.o core/imc.o core/mem.o core/buf.o
static udi_index_t _get_token_idx(const char *str, const char **outstr)
{
char *end;
- int ret = strtol(str, &end, 10);
- if( ret < 0 || ret > 255 ) {
- Log_Notice("UDI", "Value '%.*s' out of range for udi_index_t",
- end-str, str);
+ unsigned long ret = strtoul(str, &end, 10);
+ if( ret > 255 ) {
+ Log_Notice("UDI", "Value '%.*s' (0x%lx) out of range for udi_index_t",
+ end-str, str, ret);
*outstr = NULL;
return 0;
}
char *end;
unsigned long ret = strtoul(str, &end, 10);
if( ret > 0xFFFF ) {
- Log_Notice("UDI", "Value '%.*s' out of range for udi_ubit16_t",
- end-str, str);
+ Log_Notice("UDI", "Value '%.*s' (0x%lx) out of range for udi_ubit16_t",
+ end-str, str, ret);
*outstr = NULL;
return 0;
}
int child_bind_index = 0;
int device_index = 0;
int next_unpop_region = 1;
+ #define IF_ERROR(op) if(!str){error_hit=1;op;}
for( int i = 0; i < nLines; i ++ )
{
const char *str = udipropsptrs[i];
if( !*str )
continue ;
int sym = _get_token_sym_v(str, &str, true, caUDI_UdipropsNames);
+ //LOG("Processing option '%s'", (sym >= 0 ? caUDI_UdipropsNames[sym] : "ERR"));
switch(sym)
{
case UDIPROPS__properties_version:
case UDIPROPS__meta: {
tUDI_MetaLangRef *ml = &driver_module->MetaLangs[ml_index++];
ml->meta_idx = _get_token_idx(str, &str);
- if( !str ) continue;
+ IF_ERROR(continue);
ml->interface_name = str;
// TODO: May need to trim trailing spaces
ml->metalang = UDI_int_GetMetaLangByName(ml->interface_name);
tUDI_PropMessage *msg = &driver_module->Messages[msg_index++];
msg->locale = cur_locale;
msg->index = _get_token_uint16(str, &str);
- if( !str ) continue ;
+ IF_ERROR(continue);
msg->string = str;
//Log_Debug("UDI", "Message %i/%i: '%s'", msg->locale, msg->index, msg->string);
break;
case UDIPROPS__region:
{
udi_index_t rgn_idx = _get_token_idx(str, &str);
- if( !str ) continue ;
+ IF_ERROR(continue);
// Search for region index (just in case internal_bind_ops appears earlier)
tUDI_PropRegion *rgn = &driver_module->RegionTypes[0];
if( rgn_idx > 0 )
rgn->OverrunTime = _get_token_uint32(str, &str);
break;
}
- if( !str ) break ;
+ IF_ERROR(break);
}
break;
}
{
tUDI_BindOps *bind = &driver_module->Parents[parent_index++];
bind->meta_idx = _get_token_idx(str, &str);
- if( !str ) continue ;
+ IF_ERROR(continue);
bind->region_idx = _get_token_idx(str, &str);
- if( !str ) continue ;
+ IF_ERROR(continue);
bind->ops_idx = _get_token_idx(str, &str);
- if( !str ) continue ;
+ IF_ERROR(continue);
bind->bind_cb_idx = _get_token_idx(str, &str);
+ IF_ERROR(continue);
if( *str ) {
// Expected EOL, didn't get it :(
}
{
// Get region using index
udi_index_t meta = _get_token_idx(str, &str);
- if( !str ) continue ;
+ IF_ERROR(continue);
udi_index_t rgn_idx = _get_token_idx(str, &str);
- if( !str ) continue ;
+ IF_ERROR(continue);
// Search for region index (just in case the relevant 'region' comes after)
tUDI_PropRegion *rgn = &driver_module->RegionTypes[0];
rgn->BindMeta = meta;
rgn->PriBindOps = _get_token_idx(str, &str);
- if( !str ) continue ;
+ IF_ERROR(continue);
rgn->SecBindOps = _get_token_idx(str, &str);
- if( !str ) continue ;
+ IF_ERROR(continue);
rgn->BindCb = _get_token_idx(str, &str);
- if( !str ) continue ;
+ IF_ERROR(continue);
if( *str ) {
// TODO: Please sir, I want an EOL
}
{
tUDI_BindOps *bind = &driver_module->ChildBindOps[child_bind_index++];
bind->meta_idx = _get_token_idx(str, &str);
- if( !str ) continue ;
+ IF_ERROR(continue);
bind->region_idx = _get_token_idx(str, &str);
- if( !str ) continue ;
+ IF_ERROR(continue);
bind->ops_idx = _get_token_idx(str, &str);
+ IF_ERROR(continue);
if( *str ) {
// Expected EOL, didn't get it :(
}
{
int n_attr = 0;
// Count properties (and validate)
- _get_token_idx(str, &str); // message
- if( !str ) continue;
+ _get_token_uint16(str, &str); // message
+ IF_ERROR(continue);
_get_token_idx(str, &str); // meta
- if( !str ) continue;
+ IF_ERROR(continue);
while( *str )
{
_get_token_str(str, &str, NULL);
- if( !str ) break;
+ IF_ERROR(break);
_get_token_sym(str, &str, true, "string", "ubit32", "boolean", "array", NULL);
- if( !str ) break;
+ IF_ERROR(break);
_get_token_str(str, &str, NULL);
- if( !str ) break;
+ IF_ERROR(break);
n_attr ++;
}
// Rewind and actually parse
+ // - Eat the 'device' token and hence reset 'str'
_get_token_str(udipropsptrs[i], &str, NULL);
tUDI_PropDevSpec *dev = NEW_wA(tUDI_PropDevSpec, Attribs, n_attr);
driver_module->Devices[device_index++] = dev;;
- dev->MessageNum = _get_token_idx(str, &str);
+ dev->MessageNum = _get_token_uint16(str, &str);
dev->MetaIdx = _get_token_idx(str, &str);
dev->nAttribs = n_attr;
n_attr = 0;
{
udi_instance_attr_list_t *at = &dev->Attribs[n_attr];
_get_token_str(str, &str, at->attr_name);
- if( !str ) break;
+ IF_ERROR(break);
at->attr_type = _get_token_sym(str, &str, true,
" ", "string", "array", "ubit32", "boolean", NULL);
- if( !str ) break;
+ IF_ERROR(break);
udi_ubit32_t val;
switch( dev->Attribs[n_attr].attr_type )
{
UDI_ATTR32_SET(at->attr_value, _get_token_bool(str, &str));
break;
}
- if( !str ) break;
+ IF_ERROR(break);
n_attr ++;
}
}
free(udipropsptrs);
if( error_hit ) {
- Log_Error("UDI", "Error encountered while parsing udiprops for '%s' (%p), bailing",
+ Log_Error("UDI", "Error encountered while parsing udiprops for '%s' (LoadBase=%p), bailing",
driver_module->ModuleName, LoadBase);
for( int i = 0; i < device_index; i ++ )
free(driver_module->Devices[i]);
free(driver_module);
return NULL;
}
+ ASSERTC(device_index, ==, driver_module->nDevices);
- for( int i = 0; i < driver_module->nDevices; i ++ )
+ for( int i = 0; i < driver_module->nDevices; i ++ ) {
+ ASSERT(driver_module);
+ ASSERT(driver_module->Devices[i]);
driver_module->Devices[i]->Metalang = UDI_int_GetMetaLang(driver_module,
driver_module->Devices[i]->MetaIdx);
+ }
// Sort message list
// TODO: Sort message list
--- /dev/null
+/*
+ * Acess2 UDI Layer
+ * - By John Hodge (thePowersGang)
+ *
+ * trans/gfx.c
+ * - Graphics interface
+ */
+#define DEBUG 1
+#define UDI_VERSION 0x101
+#define UDI_GFX_VERSION 0x101
+#include <udi.h>
+#include <udi_gfx.h>
+#include <acess.h>
+#include <api_drv_video.h>
+
+// === TYPES ===
+typedef struct rdata_s
+{
+
+} rdata_t;
+
+// === PROTOTYPES ===
+// --- Management metalang
+void acessgfx_usage_ind(udi_usage_cb_t *cb, udi_ubit8_t resource_level);
+void acessgfx_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID);
+void acessgfx_final_cleanup_req(udi_mgmt_cb_t *cb);
+// --- GFX Binding
+void acessgfx_channel_event_ind(udi_channel_event_cb_t *cb);
+void acessgfx_bind_ack(udi_gfx_bind_cb_t *cb, udi_index_t sockets, udi_index_t engines, udi_status_t status);
+void acessgfx_unbind_ack(udi_gfx_bind_cb_t *cb);
+void acessgfx_set_connector_ack(udi_gfx_state_cb_t *cb);
+void acessgfx_get_connector_ack(udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+void acessgfx_range_connector_ack(udi_gfx_range_cb_t *cb);
+void acessgfx_set_engine_ack(udi_gfx_state_cb_t *cb);
+void acessgfx_get_engine_ack(udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+void acessgfx_range_engine_ack(udi_gfx_range_cb_t *cb);
+void acessgfx_command_ack(udi_gfx_command_cb_t *cb);
+
+// === CODE ===
+void acessgfx_usage_ind(udi_usage_cb_t *cb, udi_ubit8_t resource_level)
+{
+ rdata_t *rdata = UDI_GCB(cb)->context;
+
+ // Set up structures that don't need interegating the card to do
+
+ udi_usage_res(cb);
+}
+
+void acessgfx_channel_event_ind(udi_channel_event_cb_t *cb)
+{
+ rdata_t *rdata = UDI_GCB(cb)->context;
+
+ switch(cb->event)
+ {
+ case UDI_CHANNEL_CLOSED:
+ udi_channel_event_complete(cb);
+ return;
+ case UDI_CHANNEL_BOUND:
+ rdata->active_cb = UDI_GCB(cb);
+ acessgfx_channel_event_ind$bound(cb->params.parent_bound.bind_cb);
+ return;
+ default:
+ // TODO: emit an error of some form?
+ return;
+ }
+}
+
+void acessgfx_channel_event_ind$bound(udi_gfx_bind_cb_t *cb)
+{
+ rdata_t *rdata = UDI_GCB(bind_cb)->context;
+
+ // request metalanguage-level bind
+ udi_gfx_bind_req(bind_cb)
+ // Continued in acessgfx_bind_ack
+}
+
+void acessgfx_bind_ack(udi_gfx_bind_cb_t *cb, udi_index_t sockets, udi_index_t engines, udi_status_t status)
+{
+ rdata_t *rdata = UDI_GCB(bind_cb)->context;
+
+ if( status != UDI_OK ) {
+ // binding failed
+
+ return ;
+ }
+
+ //
+}
+
+// === UDI Bindings ===
+
--- /dev/null
+/*
+ * Acess2 UDI Layer
+ * - By John Hodge (thePowersGang)
+ *
+ * trans/gfx.c
+ * - Graphics interface
+ */
+#define DEBUG 1
+#define UDI_VERSION 0x101
+#define UDI_GFX_VERSION 0x101
+#include <udi.h>
+#include <udi_gfx.h>
+#include <acess.h>
+#include <api_drv_video.h>
+
+// === TYPES ===
+typedef struct rdata_s
+{
+
+} rdata_t;
+
+// === CODE ===
+void usage_ind(udi_usage_cb_t *cb, udi_ubit8_t resource_level)
+{
+ // Set up structures that don't need interegating the card to do
+
+}
+
+@GFX_CLIENT channel_event_ind(udi_channel_event_cb_t *cb)
+{
+ udi_gfx_bind_cb_t *bind_cb = cb->params.parent_bound.bind_cb;
+ switch(cb->event)
+ {
+ case UDI_CHANNEL_CLOSED:
+ return;
+ case UDI_CHANNEL_BOUND:
+ [sockets, engines, status] = udi_gfx_bind_req(bind_cb);
+ if( status != UDI_OK ) {
+ return;
+ }
+ // allocate a CB
+ [new_cb] = udi_cb_alloc(bind_cb, ACESSGFX_CB_STATE);
+ udi_gfx_state_cb_t *state_cb = new_cb;
+ for( int i = 0; i < engines; i ++ )
+ {
+ state_cb->subsystem = i;
+ state_cb->attribute = UDI_GFX_PROP_PIXELFORMAT;
+ [pixfmt] = udi_gfx_get_engine_req(state_cb);
+
+ // TODO: Annotate udi_gfx_state_cb_t to note values are kept?
+ state_cb->subsystem = i;
+ state_cb->attribute = UDI_GFX_PROP_OPERATOR_INDEX;
+ [count] = udi_gfx_get_engine_req(state_cb);
+ for( int j = 0; j < count; j ++ )
+ {
+ state_cb->attribute = j;
+ [operator, arg_1, arg_2, arg_3] = udi_gfx_engine_getop_req(state_cb);
+ Log_Debug("UDIGFX", "%i/%i: %i 0x%x 0x%x 0x%x",
+ i, j,
+ operator, arg_1, arg_2, arg_3);
+ }
+ }
+ return;
+ default:
+ // TODO: emit an error of some form?
+ return;
+ }
+}
+
+// === UDI Bindings ===
+
--- /dev/null
+
+Upon binding:
+
+1. Enumerate connectors (types and possible inputs)
+2. For all engines that can have their input be -1, get pixel format
+ a. Parse operator list into a tree
+3. Create MIN(n_conns,n_edge) video devices
+ a. Each device represents an edge engine (engine selected on modeset)
+
+Notes:
+- Need to figure out what engine(s) to use as framebuffer, and what ones to use as cursor
+ > Allow user/admin intervention to set these relations (based on an identifier from the driver)
clean: clean-kmode clean-user
install: install-Filesystem SyscallList install-user install-kmode
-utest: $(USRLIBS:%=utest-%)
+utest-build: $(USRLIBS:%=utest-build-%)
+utest-run: $(USRLIBS:%=utest-run-%)
+utest: utest-build utest-run
-$(USRLIBS:%=utest-%): utest-%:
+utest-build-%:
@CC=$(NCC) $(SUBMAKE) -C Usermode/Libraries/$*_src generate_exp
- @CC=$(NCC) $(SUBMAKE) -C Usermode/Libraries/$*_src utest -k
+ @CC=$(NCC) $(SUBMAKE) -C Usermode/Libraries/$*_src utest-build
+utest-run-%:
+ @CC=$(NCC) $(SUBMAKE) -C Usermode/Libraries/$*_src utest-run -k
# TODO: Module tests using DiskTool and NetTest
-mtest:
+mtest: mtest-build mtest-run
@echo > /dev/null
+mtest-build:
+ # Network
+ @CC=$(NCC) $(SUBMAKE) -C Tools/nativelib
+ @CC=$(NCC) $(SUBMAKE) -C Tools/NetTest
+ @CC=$(NCC) $(SUBMAKE) -C Tools/NetTest_Runner
+mtest-run:
+ @echo "=== Network Module Test ==="
+ @cd Tools && ./nettest_runner
SyscallList: include/syscalls.h
include/syscalls.h: KernelLand/Kernel/Makefile KernelLand/Kernel/syscalls.lst
N_OBJ = main.o tap.o mode_cmdline.o
# Compilation Options
-CFLAGS := -Wall -std=gnu99 -g -Werror -O0 -pthread
+CFLAGS := -Wall -std=gnu99 -g -O0 -pthread
CPPFLAGS := -I include/ -I ../nativelib/include
K_CPPFLAGS := -I $(KERNEL_SRC)include -I $(MODULE_SRC) -I ../../Usermode/Libraries/ld-acess.so_src/include_exp/
LDFLAGS += -Wl,--defsym,__buildnum=$(BUILD_NUM) -g -L .. -lpthread -lnativelib
OBJ += test_arp.o test_tcp.o
BIN := ../nettest_runner
-CFLAGS := -Wall -std=c99
+CFLAGS := -Wall -std=c99 -g
CPPFLAGS := -Iinclude
LIBS :=
@echo [CC] -o $@
@$(CC) $(LINKFLAGS) -o $@ $(OBJ) $(LIBS)
-obj/%.o: %.c
+obj/%.o: %.c Makefile
@mkdir -p $(dir $@)
@echo [CC] -c -o $@
@$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< -MMD -MP
extern bool Test_ARP_Basic(void);
extern bool Test_TCP_Basic(void);
+extern bool Test_TCP_Reset(void);
extern bool Test_TCP_WindowSizes(void);
#endif
Test_ARP_Basic,
Test_TCP_Basic,
Test_TCP_WindowSizes,
+ Test_TCP_Reset,
NULL
};
+ // Truncate the two output files
// TODO: Move to stack.c
- FILE *fp;
- fp = fopen("stdout.txt", "w"); fclose(fp);
- fp = fopen("stderr.txt", "w"); fclose(fp);
+ fclose( fopen("stdout.txt", "w") );
+ fclose( fopen("stderr.txt", "w") );
Net_Open(0, "/tmp/acess2net");
+ int n_pass = 0;
+ int n_fail = 0;
for(int i = 0; tests[i]; i ++ )
{
Stack_AddDevice("/tmp/acess2net", (char[]){TEST_MAC});
if( Net_Receive(0, 1, &argc, 1000) == 0 )
goto teardown;
- if( tests[i]() )
- printf("%s: PASS\n", gsTestName);
+ bool result = tests[i]();
+
+ printf("%s: %s\n", gsTestName, (result ? "PASS" : "FAIL"));
+ if(result)
+ n_pass ++;
else
- printf("%s: FAIL\n", gsTestName);
+ n_fail ++;
teardown:
Stack_Kill();
}
Net_Close(0);
unlink("/tmp/acess2net");
+ printf("--- All tests done %i pass, %i fail\n", n_pass, n_fail);
- return 0;
+ return n_fail;
}
void PrintUsage(const char *ProgName)
#include <sys/select.h>
#include "net.h"
#include <stdint.h>
+#include <unistd.h> // unlink/...
+#include <sys/time.h> // gettimeofday
#define CONNECT_TIMEOUT 10*1000
#define MAX_IFS 4
if( Net_int_EnsureConnected(IfNum) && WaitOnFD(If->FD, false, Timeout) )
{
- size_t rv = recvfrom(If->FD, DestBuf, MaxLen, 0, &If->addr, &If->addrlen);
+ size_t rv = recvfrom(If->FD, DestBuf, MaxLen, 0, (struct sockaddr*)&If->addr, &If->addrlen);
Net_int_SavePacket(If, rv, DestBuf);
return rv;
}
if( !WaitOnFD(If->FD, true, CONNECT_TIMEOUT) )
return ;
Net_int_SavePacket(If, Length, Buf);
- int rv = sendto(If->FD, Buf, Length, 0, &If->addr, If->addrlen);
+ int rv = sendto(If->FD, Buf, Length, 0, (struct sockaddr*)&If->addr, If->addrlen);
if( rv < 0 )
perror("Net_Send - send");
}
break;
default:
assert(AddrType == 4);
+ fprintf(stderr, "Stack_AddInterface: Bad address type %i\n", AddrType);
exit(1);
}
}
void Stack_AddArg(const char *Fmt, ...)
{
+ if( giNumStackArgs == MAX_ARGS ) {
+ fprintf(stderr, "ERROR: Too many arguments to stack, %i max\n", MAX_ARGS);
+ return ;
+ }
va_list args;
+
va_start(args, Fmt);
size_t len = vsnprintf(NULL, 0, Fmt, args);
va_end(args);
+
char *arg = malloc(len+1);
+ assert(arg);
+
va_start(args, Fmt);
vsnprintf(arg, len+1, Fmt, args);
va_end(args);
+
+ assert( arg[len] == '\0' );
gasStackArgs[giNumStackArgs++] = arg;
}
kill(giStack_PID, SIGTERM);
giStack_PID = 0;
}
+
+ for( int i = 1; i < giNumStackArgs; i ++ )
+ free(gasStackArgs[i]);
+ giNumStackArgs = 1;
}
int Stack_SendCommand(const char *Fmt, ...)
#include "tcp.h"
#include <string.h>
+#define RX_HEADER \
+ size_t rxlen, ofs, len; \
+ char rxbuf[MTU]
+#define TEST_HEADER \
+ TEST_SETNAME(__func__);\
+ RX_HEADER
+
#define TEST_ASSERT_rx() TEST_ASSERT( rxlen = Net_Receive(0, sizeof(rxbuf), rxbuf, ERX_TIMEOUT) )
#define TEST_ASSERT_no_rx() TEST_ASSERT( Net_Receive(0, sizeof(rxbuf), rxbuf, NRX_TIMEOUT) == 0 )
const int ERX_TIMEOUT = 1000; // Expect RX timeout (timeout=failure)
bool Test_TCP_Basic(void)
{
- TEST_SETNAME(__func__);
- size_t rxlen, ofs, len;
- char rxbuf[MTU];
+ TEST_HEADER;
tTCPConn testconn = {
.IFNum = 0, .AF = 4,
return true;
}
+bool Test_TCP_int_OpenConnection(tTCPConn *Conn)
+{
+ RX_HEADER;
+ // >> SYN
+ TCP_SendC(Conn, TCP_SYN, 0, NULL);
+ Conn->LSeq ++;
+ TEST_ASSERT_rx();
+ // << SYN|ACK (save remote sequence number)
+ TCP_SkipCheck_Seq(true);
+ TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, Conn, TCP_SYN|TCP_ACK) );
+ TEST_ASSERT_REL(len, ==, 0);
+ Conn->RSeq = TCP_Pkt_GetSeq(rxlen, rxbuf, Conn->AF) + 1;
+ // >> ACK
+ TCP_SendC(Conn, TCP_ACK, 0, NULL);
+ TEST_ASSERT_no_rx();
+ return true;
+}
+
+#if 0
bool Test_TCP_SYN_RECEIVED(void)
{
- TEST_SETNAME(__func__);
+ TEST_HEADER;
+
// 1. Get into SYN-RECEIVED
+ TCP_SendC(&testconn, TCP_SYN, 0, NULL);
// 2. Send various non-ACK packets
return false;
}
+#endif
-bool Test_TCP_WindowSizes(void)
+bool Test_TCP_Reset(void)
{
- TEST_SETNAME(__func__);
- size_t rxlen, ofs, len;
- char rxbuf[MTU];
+ TEST_HEADER;
+
tTCPConn testconn = {
.IFNum = 0,
.AF = 4,
.RSeq = 0x600,
.Window = 128
};
- char testdata[152];
- memset(testdata, 0xAB, sizeof(testdata));
-
+
Stack_SendCommand("tcp_echo_server %i", testconn.RPort);
- // > Open Connection
+
+ // 1. Response in listen-based SYN-RECEIVED
+ // >> SYN
TCP_SendC(&testconn, TCP_SYN, 0, NULL);
testconn.LSeq ++;
+ // << SYN|ACK :: Now in SYN-RECEIVED
TEST_ASSERT_rx();
TCP_SkipCheck_Seq(true);
TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_SYN|TCP_ACK) );
TEST_ASSERT_REL(len, ==, 0);
testconn.RSeq = TCP_Pkt_GetSeq(rxlen, rxbuf, testconn.AF) + 1;
+ // >> RST (not ACK)
+ TCP_SendC(&testconn, TCP_RST, 0, NULL);
+ // << nothing (connection should now be dead)
+ TEST_ASSERT_no_rx();
+ // >> ACK (this should be to a closed conneciton, see LISTEN[ACK] above)
TCP_SendC(&testconn, TCP_ACK, 0, NULL);
+ // << RST
+ TEST_ASSERT_rx();
+ TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_RST) );
+ TEST_ASSERT_REL(len, ==, 0);
+
+ // 2. Response in open-based SYN-RECEIVED? (What is that?)
+ TEST_WARN("TODO: RFC793 pg70 mentions OPEN-based SYN-RECEIVED");
+
+ testconn.LPort += 1234;
+ // ESTABLISHED[RST] - RFC793:Pg70
+ // 2. Response in ESTABLISHED
+ TEST_ASSERT( Test_TCP_int_OpenConnection(&testconn) );
+ // >> RST
+ TCP_SendC(&testconn, TCP_RST, 0, NULL);
+ // << no response, connection closed
TEST_ASSERT_no_rx();
+ // >> ACK (LISTEN[ACK])
+ TCP_SendC(&testconn, TCP_ACK, 0, NULL);
+ // << RST
+ TEST_ASSERT_rx();
+ TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_RST) );
+ TEST_ASSERT_REL(len, ==, 0);
+
+ return true;
+}
+
+bool Test_TCP_WindowSizes(void)
+{
+ TEST_HEADER;
+ tTCPConn testconn = {
+ .IFNum = 0,
+ .AF = 4,
+ .LAddr = BLOB(HOST_IP),
+ .RAddr = BLOB(TEST_IP),
+ .LPort = 44359,
+ .RPort = 9,
+ .LSeq = 0x600,
+ .RSeq = 0x600,
+ .Window = 128
+ };
+ char testdata[152];
+ memset(testdata, 0xAB, sizeof(testdata));
+
+ Stack_SendCommand("tcp_echo_server %i", testconn.RPort);
+ // > Open Connection
+ TEST_ASSERT( Test_TCP_int_OpenConnection(&testconn) );
// 1. Test remote respecting our transmission window (>=1 byte)
// > Send more data than our RX window
OBJ := $(NOBJ) $(LOBJ) $(KOBJ)
BIN := ../libnativelib.a
-CFLAGS := -Wall -std=c99 -Werror
+CFLAGS := -Wall -std=c99
CPPFLAGS := -I include
extern int Threads_int_ThreadingEnabled(void);
-extern tThread *Proc_GetCurThread(void);
-extern tThread *Threads_RemActive(void);
-extern void Threads_AddActive(tThread *Thread);
+extern struct sThread *Proc_GetCurThread(void);
+extern struct sThread *Threads_RemActive(void);
+extern void Threads_AddActive(struct sThread *Thread);
extern void Threads_int_WaitForStatusEnd(enum eThreadStatus Status);
-extern int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock);
-extern void Semaphore_ForceWake(tThread *Thread);
+extern int Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, struct sThread **ListHead, struct sThread **ListTail, tShortSpinlock *Lock);
+extern void Semaphore_ForceWake(struct sThread *Thread);
extern tThreadIntMutex *Threads_int_MutexCreate(void);
extern void Threads_int_MutexDestroy(tThreadIntMutex *Mutex);
--- /dev/null
+/*
+ * UDI Bochs Graphics Driver
+ * By John Hodge (thePowersGang)
+ *
+ * bochsga_common.c
+ * - Common definitions
+ */
+#ifndef _BOCHSGA_COMMON_H_
+#define _BOCHSGA_COMMON_H_
+
+/**
+ * Definitions to match udiprops.txt
+ * \{
+ */
+#define BOCHSGA_META_BUS 1
+#define BOCHSGA_META_GFX 2
+
+#define BOCHSGA_OPS_DEV 1
+#define BOCHSGA_OPS_GFX 2
+
+#define BOCHSGA_CB_BUS_BIND 1
+#define BOCHSGA_CB_GFX_BIND 2
+#define BOCHSGA_CB_GFX_STATE 3
+#define BOCHSGA_CB_GFX_RANGE 4
+#define BOCHSGA_CB_GFX_COMMAND 5
+
+#define BOCHSGA_MSGNUM_PROPUNK 1001
+/**
+ * \}
+ */
+
+#include "bochsga_pio.h"
+#include "bochsga_engines.h"
+
+typedef struct {
+ udi_ubit32_t width;
+ udi_ubit32_t height;
+ udi_index_t op_idx;
+} engine_t;
+
+/**
+ * Region data
+ */
+typedef struct
+{
+ udi_cb_t *active_cb;
+ struct {
+ udi_index_t pio_index;
+ } init;
+
+ udi_pio_handle_t pio_handles[N_PIO];
+
+ udi_boolean_t output_enable;
+ struct {
+ udi_ubit32_t width;
+ udi_ubit32_t height;
+ udi_ubit8_t bitdepth;
+ udi_index_t engine;
+ } outputstate;
+ struct {
+ udi_ubit32_t max_width; // 1024 or 1280
+ udi_ubit32_t max_height; // 768 or 1024
+ } limits;
+
+ engine_t engines[N_ENGINES];
+} rdata_t;
+
+#define BOCHSGA_MIN_WIDTH 360
+#define BOCHSGA_MIN_HEIGHT 240
+
+#endif
+
--- /dev/null
+/*
+ * UDI Bochs Graphics Driver
+ * By John Hodge (thePowersGang)
+ *
+ * bochsga_core.c
+ * - Core Code
+ */
+#define UDI_VERSION 0x101
+#define UDI_GFX_VERSION 0x101
+#include <udi.h>
+#include <udi_physio.h>
+#include <udi_gfx.h>
+#define DEBUG_ENABLED 1
+#include "../helpers.h"
+#include "../helpers_gfx.h"
+#include "bochsga_common.h"
+
+// --- Management Metalang
+void bochsga_usage_ind(udi_usage_cb_t *cb, udi_ubit8_t resource_level)
+{
+ rdata_t *rdata = UDI_GCB(cb)->context;
+ //udi_trace_write(rdata->init_context, UDI_TREVENT_LOCAL_PROC_ENTRY, 0, );
+
+ // TODO: Set up region data
+
+ udi_usage_res(cb);
+}
+
+void bochsga_enumerate_req(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level)
+{
+ rdata_t *rdata = UDI_GCB(cb)->context;
+ udi_instance_attr_list_t *attr_list = cb->attr_list;
+
+ switch(enumeration_level)
+ {
+ case UDI_ENUMERATE_START:
+ case UDI_ENUMERATE_START_RESCAN:
+ cb->attr_valid_length = attr_list - cb->attr_list;
+ udi_enumerate_ack(cb, UDI_ENUMERATE_OK, BOCHSGA_OPS_GFX);
+ break;
+ case UDI_ENUMERATE_NEXT:
+ udi_enumerate_ack(cb, UDI_ENUMERATE_DONE, 0);
+ break;
+ }
+}
+void bochsga_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID)
+{
+}
+void bochsga_final_cleanup_req(udi_mgmt_cb_t *cb)
+{
+}
+// ---
+void bochsga_bus_dev_channel_event_ind(udi_channel_event_cb_t *cb);
+void bochsga_bus_dev_bus_bind_ack(udi_bus_bind_cb_t *cb, udi_dma_constraints_t dma_constraints, udi_ubit8_t perferred_endianness, udi_status_t status);
+void bochsga_bus_dev_bind__pio_map(udi_cb_t *cb, udi_pio_handle_t new_pio_handle);
+void bochsga_bus_dev_bind__intr_chanel(udi_cb_t *gcb, udi_channel_t new_channel);
+void bochsga_bus_dev_bus_unbind_ack(udi_bus_bind_cb_t *cb);
+
+void bochsga_bus_dev_channel_event_ind(udi_channel_event_cb_t *cb)
+{
+ udi_cb_t *gcb = UDI_GCB(cb);
+ rdata_t *rdata = gcb->context;
+
+ switch(cb->event)
+ {
+ case UDI_CHANNEL_CLOSED:
+ break;
+ case UDI_CHANNEL_BOUND: {
+ rdata->active_cb = gcb;
+ udi_bus_bind_cb_t *bus_bind_cb = UDI_MCB(cb->params.parent_bound.bind_cb, udi_bus_bind_cb_t);
+ udi_bus_bind_req( bus_bind_cb );
+ // continue at bochsga_bus_dev_bus_bind_ack
+ return; }
+ }
+}
+void bochsga_bus_dev_bus_bind_ack(udi_bus_bind_cb_t *cb,
+ udi_dma_constraints_t dma_constraints, udi_ubit8_t perferred_endianness, udi_status_t status)
+{
+ udi_cb_t *gcb = UDI_GCB(cb);
+ rdata_t *rdata = gcb->context;
+
+ // Set up PIO handles
+ rdata->init.pio_index = -1;
+ bochsga_bus_dev_bind__pio_map(gcb, UDI_NULL_PIO_HANDLE);
+ // V V V V
+}
+void bochsga_bus_dev_bind__pio_map(udi_cb_t *gcb, udi_pio_handle_t new_pio_handle)
+{
+ rdata_t *rdata = gcb->context;
+ if( rdata->init.pio_index != -1 )
+ {
+ rdata->pio_handles[rdata->init.pio_index] = new_pio_handle;
+ }
+
+ rdata->init.pio_index ++;
+ if( rdata->init.pio_index < N_PIO )
+ {
+ const struct s_pio_ops *ops = &bochsga_pio_ops[rdata->init.pio_index];
+ udi_pio_map(bochsga_bus_dev_bind__pio_map, gcb,
+ ops->regset_idx, ops->base_offset, ops->length,
+ ops->trans_list, ops->list_length,
+ UDI_PIO_LITTLE_ENDIAN, 0, 0
+ );
+ return ;
+ }
+
+ udi_channel_event_complete( UDI_MCB(rdata->active_cb, udi_channel_event_cb_t), UDI_OK);
+ // = = = = =
+}
+void bochsga_bus_dev_bus_unbind_ack(udi_bus_bind_cb_t *cb)
+{
+}
+void bochsga_bus_dev_intr_attach_ack(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status)
+{
+}
+void bochsga_bus_dev_intr_detach_ack(udi_intr_detach_cb_t *intr_detach_cb)
+{
+}
+// ---
+// GFX Provider ops
+void bochsga_gfx_channel_event_ind(udi_channel_event_cb_t *cb)
+{
+ // No operation
+}
+void bochsga_gfx_bind_req(udi_gfx_bind_cb_t *cb)
+{
+ // TODO: ACK bind if nothing already bound
+}
+void bochsga_gfx_unbind_req(udi_gfx_bind_cb_t *cb)
+{
+ // TODO: Release internal state?
+}
+void bochsga_gfx_set_connector_req$pio(udi_cb_t *gcb, udi_buf_t *new_buf, udi_status_t status, udi_ubit16_t result)
+{
+ udi_gfx_state_cb_t *cb = UDI_MCB(gcb, udi_gfx_state_cb_t);
+ udi_gfx_set_connector_ack(cb);
+}
+void bochsga_gfx_set_connector_req(udi_gfx_state_cb_t *cb, udi_ubit32_t value)
+{
+ udi_cb_t *gcb = UDI_GCB(cb);
+ rdata_t *rdata = gcb->context;
+
+ switch(cb->attribute)
+ {
+ case UDI_GFX_PROP_ENABLE:
+ if( rdata->output_enable != !!value )
+ {
+ rdata->output_enable = !!value;
+ udi_pio_trans(bochsga_gfx_set_connector_req$pio, gcb,
+ rdata->pio_handles[BOCHSGA_PIO_ENABLE], rdata->output_enable, NULL, &rdata->outputstate);
+ return ;
+ }
+ udi_gfx_set_connector_ack(cb);
+ return;
+ // Change input engine
+ // -
+ case UDI_GFX_PROP_INPUT:
+ if( rdata->outputstate.engine != value )
+ {
+ // Validate
+ if( !(0 <= value && value <= N_ENGINES) ) {
+ udi_gfx_set_connector_ack(cb /*, UDI_STAT_NOT_SUPPORTED*/);
+ return ;
+ }
+
+ // Change saved bitdepth (requires cycling enable)
+ rdata->outputstate.engine = value;
+ rdata->outputstate.bitdepth = bochsga_engine_defs[value].bitdepth;
+ }
+ udi_gfx_set_connector_ack(cb);
+ return;
+ // Alter output dimensions
+ case UDI_GFX_PROP_WIDTH:
+ if( value % 8 != 0 ) {
+ // Qemu doesn't like resolutions not a multiple of 8
+ return ;
+ }
+ if( !(320 <= value && value <= rdata->limits.max_width) ) {
+ return ;
+ }
+ rdata->outputstate.width = value;
+ udi_pio_trans(bochsga_gfx_set_connector_req$pio, gcb,
+ rdata->pio_handles[BOCHSGA_PIO_ENABLE], rdata->output_enable, NULL, &rdata->outputstate);
+ return;
+ case UDI_GFX_PROP_HEIGHT:
+ if( !(240 <= value && value <= rdata->limits.max_height) ) {
+ return ;
+ }
+ rdata->outputstate.height = value;
+ udi_pio_trans(bochsga_gfx_set_connector_req$pio, gcb,
+ rdata->pio_handles[BOCHSGA_PIO_ENABLE], rdata->output_enable, NULL, &rdata->outputstate);
+ return;
+ }
+ CONTIN(bochsga_gfx_set_connector_req, udi_log_write,
+ (UDI_TREVENT_LOG, UDI_LOG_INFORMATION, BOCHSGA_OPS_GFX, 0, BOCHSGA_MSGNUM_PROPUNK, __func__, cb->attribute),
+ (udi_status_t status)
+ );
+ udi_gfx_state_cb_t *cb = UDI_MCB(cb, udi_gfx_state_cb_t);
+ udi_gfx_set_connector_ack(cb /*, UDI_STAT_NOT_SUPPORTED*/);
+}
+void bochsga_gfx_get_connector_req(udi_gfx_state_cb_t *cb)
+{
+ udi_cb_t *gcb = UDI_GCB(cb);
+ rdata_t *rdata = gcb->context;
+
+ switch(cb->attribute)
+ {
+ case UDI_GFX_PROP_ENABLE:
+ udi_gfx_get_connector_ack(cb, !!rdata->output_enable);
+ return;
+ case UDI_GFX_PROP_INPUT:
+ udi_gfx_get_connector_ack(cb, rdata->outputstate.bitdepth/8-1);
+ return;
+ case UDI_GFX_PROP_WIDTH:
+ udi_gfx_get_connector_ack(cb, rdata->outputstate.width);
+ return;
+ case UDI_GFX_PROP_HEIGHT:
+ udi_gfx_get_connector_ack(cb, rdata->outputstate.height);
+ return;
+ case UDI_GFX_PROP_CONNECTOR_TYPE:
+ udi_gfx_get_connector_ack(cb, UDI_GFX_CONNECTOR_HIDDEN);
+ return;
+ case UDI_GFX_PROP_SIGNAL:
+ udi_gfx_get_connector_ack(cb, UDI_GFX_SIGNAL_INTEGRATED);
+ return;
+ }
+ CONTIN(bochsga_gfx_get_connector_req, udi_log_write,
+ (UDI_TREVENT_LOG, UDI_LOG_INFORMATION, BOCHSGA_OPS_GFX, 0, BOCHSGA_MSGNUM_PROPUNK, __func__, cb->attribute),
+ (udi_status_t status)
+ );
+ udi_gfx_state_cb_t *cb = UDI_MCB(cb, udi_gfx_state_cb_t);
+ udi_gfx_get_connector_ack(cb, 0);
+}
+void bochsga_gfx_range_connector_req(udi_gfx_range_cb_t *cb)
+{
+ udi_cb_t *gcb = UDI_GCB(cb);
+ rdata_t *rdata = gcb->context;
+
+ switch(cb->attribute)
+ {
+ case UDI_GFX_PROP_ENABLE:
+ // 2 values: 0 and 1
+ gfxhelpers_return_range_set(udi_gfx_range_connector_ack, cb, 2, 0, 1);
+ return;
+ case UDI_GFX_PROP_INPUT:
+ // 0--3 with a step of 1
+ gfxhelpers_return_range_simple(udi_gfx_range_connector_ack, cb, 0, 3, 1);
+ return;
+ case UDI_GFX_PROP_WIDTH:
+ gfxhelpers_return_range_simple(udi_gfx_range_connector_ack, cb,
+ BOCHSGA_MIN_WIDTH, rdata->limits.max_width, 8); // qemu restricts to 8 step
+ return;
+ case UDI_GFX_PROP_HEIGHT:
+ gfxhelpers_return_range_simple(udi_gfx_range_connector_ack, cb,
+ BOCHSGA_MIN_HEIGHT, rdata->limits.max_height, 8); // step of 8 for neatness
+ return;
+ case UDI_GFX_PROP_CONNECTOR_TYPE:
+ gfxhelpers_return_range_fixed(udi_gfx_range_connector_ack, cb, UDI_GFX_CONNECTOR_HIDDEN);
+ return;
+ case UDI_GFX_PROP_SIGNAL:
+ gfxhelpers_return_range_fixed(udi_gfx_range_connector_ack, cb, UDI_GFX_SIGNAL_INTEGRATED);
+ return;
+ }
+ CONTIN(bochsga_gfx_range_connector_req, udi_log_write,
+ (UDI_TREVENT_LOG, UDI_LOG_INFORMATION, BOCHSGA_OPS_GFX, 0, BOCHSGA_MSGNUM_PROPUNK, __func__, cb->attribute),
+ (udi_status_t status)
+ );
+ udi_gfx_range_cb_t *cb = UDI_MCB(cb, udi_gfx_range_cb_t);
+ udi_gfx_range_connector_ack(cb);
+}
+// --- Engine Manipulation ---
+void bochsga_gfx_set_engine_req(udi_gfx_state_cb_t *cb, udi_ubit32_t value)
+{
+ udi_cb_t *gcb = UDI_GCB(cb);
+ rdata_t *rdata = gcb->context;
+
+ if( cb->subsystem >= N_ENGINES ) {
+ udi_gfx_get_engine_ack(cb, 0);
+ return;
+ }
+
+ engine_t *engine = &rdata->engines[cb->subsystem];
+
+ switch(cb->attribute)
+ {
+ case UDI_GFX_PROP_WIDTH:
+ engine->width = value;
+ udi_gfx_set_engine_ack(cb);
+ return;
+ case UDI_GFX_PROP_HEIGHT:
+ engine->height = value;
+ udi_gfx_set_engine_ack(cb);
+ return;
+ case UDI_GFX_PROP_OPERATOR_INDEX:
+ if( value >= bochsga_engine_defs[cb->subsystem].op_map.op_count ) {
+ // Bad value
+ udi_gfx_set_engine_ack(cb);
+ return;
+ }
+ engine->op_idx = value;
+ udi_gfx_set_engine_ack(cb);
+ return;
+ }
+ CONTIN(bochsga_gfx_set_engine_req, udi_log_write,
+ (UDI_TREVENT_LOG, UDI_LOG_INFORMATION, BOCHSGA_OPS_GFX, 0, BOCHSGA_MSGNUM_PROPUNK, __func__, cb->attribute),
+ (udi_status_t status)
+ );
+ udi_gfx_state_cb_t *cb = UDI_MCB(cb, udi_gfx_state_cb_t);
+ udi_gfx_set_engine_ack(cb);
+}
+void bochsga_gfx_get_engine_req(udi_gfx_state_cb_t *cb)
+{
+ udi_cb_t *gcb = UDI_GCB(cb);
+ rdata_t *rdata = gcb->context;
+
+ if( cb->subsystem >= N_ENGINES ) {
+ udi_gfx_get_engine_ack(cb, 0);
+ return;
+ }
+
+ const engine_t *engine = &rdata->engines[cb->subsystem];
+ const engine_static_t *engine_def = &bochsga_engine_defs[cb->subsystem];
+
+ switch(cb->attribute)
+ {
+ case UDI_GFX_PROP_ENABLE:
+ udi_gfx_get_engine_ack(cb, 1);
+ return;
+
+ case UDI_GFX_PROP_INPUT:
+ udi_gfx_get_engine_ack(cb, -1);
+ return;
+
+ case UDI_GFX_PROP_WIDTH:
+ udi_gfx_get_engine_ack(cb, engine->width);
+ return;
+ case UDI_GFX_PROP_HEIGHT:
+ udi_gfx_get_engine_ack(cb, engine->height);
+ return;
+
+ case UDI_GFX_PROP_OPERATOR_INDEX:
+ udi_gfx_get_engine_ack(cb, engine->op_idx);
+ return;
+ case UDI_GFX_PROP_OPERATOR_OPCODE:
+ case UDI_GFX_PROP_OPERATOR_ARG_1:
+ case UDI_GFX_PROP_OPERATOR_ARG_2:
+ case UDI_GFX_PROP_OPERATOR_ARG_3:
+ udi_gfx_get_engine_ack(cb, gfxhelpers_get_engine_op(&engine_def->op_map, engine->op_idx, cb->attribute));
+ return;
+ }
+ CONTIN(bochsga_gfx_get_engine_req, udi_log_write,
+ (UDI_TREVENT_LOG, UDI_LOG_INFORMATION, BOCHSGA_OPS_GFX, 0, BOCHSGA_MSGNUM_PROPUNK, __func__, cb->attribute),
+ (udi_status_t status)
+ );
+ udi_gfx_state_cb_t *cb = UDI_MCB(cb, udi_gfx_state_cb_t);
+ udi_gfx_get_engine_ack(cb, 0);
+}
+void bochsga_gfx_range_engine_req(udi_gfx_range_cb_t *cb)
+{
+ udi_cb_t *gcb = UDI_GCB(cb);
+ rdata_t *rdata = gcb->context;
+
+ if( cb->subsystem >= N_ENGINES ) {
+ udi_gfx_range_engine_ack(cb);
+ return;
+ }
+
+ engine_t *engine = &rdata->engines[cb->subsystem];
+ const engine_static_t *engine_def = &bochsga_engine_defs[cb->subsystem];
+
+ switch(cb->attribute)
+ {
+ case UDI_GFX_PROP_ENABLE:
+ gfxhelpers_return_range_fixed(udi_gfx_range_engine_ack, cb, 1);
+ return;
+ case UDI_GFX_PROP_INPUT:
+ gfxhelpers_return_range_fixed(udi_gfx_range_engine_ack, cb, -1);
+ return;
+
+ case UDI_GFX_PROP_OPERATOR_INDEX:
+ gfxhelpers_return_range_simple(udi_gfx_range_engine_ack, cb, 0, engine->op_idx-1, 1);
+ return;
+ case UDI_GFX_PROP_OPERATOR_OPCODE:
+ case UDI_GFX_PROP_OPERATOR_ARG_1:
+ case UDI_GFX_PROP_OPERATOR_ARG_2:
+ case UDI_GFX_PROP_OPERATOR_ARG_3:
+ gfxhelpers_return_range_fixed(udi_gfx_range_engine_ack, cb,
+ gfxhelpers_get_engine_op(&engine_def->op_map, engine->op_idx, cb->attribute));
+ return;
+ }
+ CONTIN(bochsga_gfx_range_engine_req, udi_log_write,
+ (UDI_TREVENT_LOG, UDI_LOG_INFORMATION, BOCHSGA_OPS_GFX, 0, BOCHSGA_MSGNUM_PROPUNK, __func__, cb->attribute),
+ (udi_status_t status)
+ );
+ udi_gfx_range_cb_t *cb = UDI_MCB(cb, udi_gfx_range_cb_t);
+ udi_gfx_range_engine_ack( cb );
+}
+void bochsga_gfx_command_req(udi_gfx_command_cb_t *cb)
+{
+ // Need to parse the GLX stream
+}
+
+// ====================================================================
+// - Management ops
+udi_mgmt_ops_t bochsga_mgmt_ops = {
+ bochsga_usage_ind,
+ bochsga_enumerate_req,
+ bochsga_devmgmt_req,
+ bochsga_final_cleanup_req
+};
+udi_ubit8_t bochsga_mgmt_op_flags[4] = {0,0,0,0};
+// - Bus Ops
+udi_bus_device_ops_t bochsga_bus_dev_ops = {
+ bochsga_bus_dev_channel_event_ind,
+ bochsga_bus_dev_bus_bind_ack,
+ bochsga_bus_dev_bus_unbind_ack,
+ bochsga_bus_dev_intr_attach_ack,
+ bochsga_bus_dev_intr_detach_ack
+};
+udi_ubit8_t bochsga_bus_dev_ops_flags[5] = {0};
+// - GFX provider ops
+udi_gfx_provider_ops_t bochsga_gfx_ops = {
+ bochsga_gfx_channel_event_ind,
+ bochsga_gfx_bind_req,
+ bochsga_gfx_unbind_req,
+ bochsga_gfx_set_connector_req,
+ bochsga_gfx_set_engine_req,
+ bochsga_gfx_get_connector_req,
+ bochsga_gfx_get_engine_req,
+ bochsga_gfx_range_connector_req,
+ bochsga_gfx_range_engine_req,
+ bochsga_gfx_command_req
+};
+udi_ubit8_t bochsga_gfx_ops_flags[10] = {0};
+// --
+udi_primary_init_t bochsga_pri_init = {
+ .mgmt_ops = &bochsga_mgmt_ops,
+ .mgmt_op_flags = bochsga_mgmt_op_flags,
+ .mgmt_scratch_requirement = 0,
+ .enumeration_attr_list_length = 0,
+ .rdata_size = sizeof(rdata_t),
+ .child_data_size = 0,
+ .per_parent_paths = 0
+};
+udi_ops_init_t bochsga_ops_list[] = {
+ {
+ BOCHSGA_OPS_DEV, BOCHSGA_META_BUS, UDI_BUS_DEVICE_OPS_NUM,
+ 0,
+ (udi_ops_vector_t*)&bochsga_bus_dev_ops,
+ bochsga_bus_dev_ops_flags
+ },
+ {
+ BOCHSGA_OPS_GFX, BOCHSGA_META_GFX, UDI_GFX_PROVIDER_OPS_NUM,
+ 0,
+ (udi_ops_vector_t*)&bochsga_gfx_ops,
+ bochsga_gfx_ops_flags
+ },
+ {0}
+};
+udi_cb_init_t bochsga_cb_init_list[] = {
+ {BOCHSGA_CB_BUS_BIND, BOCHSGA_META_BUS, UDI_BUS_BIND_CB_NUM, 0, 0,NULL},
+ {BOCHSGA_CB_GFX_BIND, BOCHSGA_META_GFX, UDI_GFX_BIND_CB_NUM, 0, 0,NULL},
+ {BOCHSGA_CB_GFX_STATE, BOCHSGA_META_GFX, UDI_GFX_STATE_CB_NUM, 0, 0,NULL},
+ {BOCHSGA_CB_GFX_RANGE, BOCHSGA_META_GFX, UDI_GFX_RANGE_CB_NUM, 0, 0,NULL},
+ {BOCHSGA_CB_GFX_COMMAND, BOCHSGA_META_GFX, UDI_GFX_COMMAND_CB_NUM, 0, 0,NULL},
+ {0}
+};
+const udi_init_t udi_init_info = {
+ .primary_init_info = &bochsga_pri_init,
+ .ops_init_list = bochsga_ops_list,
+ .cb_init_list = bochsga_cb_init_list,
+};
--- /dev/null
+/*
+ *
+ */
+
+#define BOCHSGA_ENGINE_PROP_BUFFER (UDI_GFX_PROP_CUSTOM+0)
+
+// === CONSTANTS ===
+const gfxhelpers_op_t bochsga_engine_ops_8bpp[] = {
+};
+const gfxhelpers_op_t bochsga_engine_ops_32bpp[] = {
+ {UDI_GFX_OPERATOR_RGB, 1, 2, 3}, // #0 Output RGB from ops #1,#2,#3
+ {UDI_GFX_OPERATOR_SEG, 4, 16, 8}, // #1 Extract 8 bits from bit 16 of #4
+ {UDI_GFX_OPERATOR_SEG, 4, 8, 8}, // #2 8 bits from ofs 8 of #4
+ {UDI_GFX_OPERATOR_SEG, 4, 0, 8}, // #3 8 bits from ofs 0 of #4
+ {UDI_GFX_OPERATOR_BUFFER, 5, 6, 32}, // #4 32 bits from buffer #5 ofs #6
+ {UDI_GFX_OPERATOR_ATTR, 0, BOCHSGA_ENGINE_PROP_BUFFER, 0}, // #5 Buffer index
+ {UDI_GFX_OPERATOR_MAD, 7, 8, 9}, // #6 Get offset (#8 * #7 + #9)
+ {UDI_GFX_OPERATOR_ATTR, 0, UDI_GFX_PROP_SOURCE_WIDTH, 0}, // #7 Read buffer width
+ {UDI_GFX_OPERATOR_Y, 0, 0, 0}, // #8 Y coordinate
+ {UDI_GFX_OPERATOR_X, 0, 0, 0} // #9 X coordinate
+};
+
+typedef struct {
+ udi_ubit8_t bitdepth;
+ gfxhelpers_op_map_t op_map;
+} engine_static_t;
+
+const engine_static_t bochsga_engine_defs[] = {
+ {.bitdepth = 8, .op_map = {ARRAY_COUNT(bochsga_engine_ops_8bpp), bochsga_engine_ops_8bpp}},
+ {.bitdepth = 16},
+ {.bitdepth = 24},
+ {.bitdepth = 32, .op_map = {ARRAY_COUNT(bochsga_engine_ops_8bpp), bochsga_engine_ops_32bpp}},
+};
+#define N_ENGINES ARRAY_COUNT(bochsga_engine_defs)
--- /dev/null
+/*
+ * TODO
+ */
+#ifndef _BOCHSGA_PIO_H_
+#define _BOCHSGA_PIO_H_
+
+udi_pio_trans_t bochsga_pio_enable[] = {
+ {UDI_PIO_END_IMM, UDI_PIO_2BYTE, 0},
+ };
+
+enum {
+ BOCHSGA_PIO_ENABLE,
+};
+
+const struct s_pio_ops bochsga_pio_ops[] = {
+ [BOCHSGA_PIO_ENABLE] = UDIH_PIO_OPS_ENTRY(bochsga_pio_enable, 0, UDI_PCI_BAR_2, 0x400, 0xB*2),
+// UDIH_PIO_OPS_ENTRY(bochsga_pio_enable, 0, UDI_PCI_BAR_2, 0x400, 0xB*2),
+ };
+#define N_PIO (sizeof(bochsga_pio_ops)/sizeof(struct s_pio_ops))
+
+#endif
+
--- /dev/null
+properties_version 0x101
+supplier 1
+contact 2
+name 3
+shortname bochsga
+release 5 1.0
+
+requires udi 0x101
+requires udi_physio 0x101
+requires udi_bridge 0x101
+requires udi_gfx 0x101
+
+meta 1 udi_bridge
+meta 2 udi_gfx
+#meta 3 udi_gio
+
+parent_bind_ops 1 0 1 1 # bridge, rgn 0, ops 1, cb 1
+child_bind_ops 2 0 2 # Provider
+#parent_bind_ops 3 0 2 3 # GIO bound to 3D provider
+
+#enumerates 102 1 1 3 gio_type string software3d
+
+# - Classic non-PCI version
+device 100 1 bus string system sysbus_io_addr_lo ubit32 0x01CE sysbus_io_size ubit32 2 sysbys_mem_addr_lo ubit32 0xE0000000 sysbus_mem_size 0x400000
+# - PCI Version (Non-indexed registers at offset 0x500 in BAR2 MMIO)
+device 101 1 bus string pci pci_vendor_id ubit32 0x1234 pci_device_id ubit32 0x1111 pci_base_class ubit32 0x03 pci_sub_clais ubit32 0x00 pci_prog_if ubit32 0x00
+
+# Messages
+message 1 John Hodge (thePowersGang)
+message 3 Bochs Graphics Adapter
+message 5 BochsGA
+message 100 BochsGA ISA Device
+message 101 BochsGA PCI Device
+
+message 1001 "Unknown property passed to %s: %i"
+
+module bochsga
+region 0
+
+# Source-only udiprops
+compile_options -Wall
+source_files bochsga_core.c
--- /dev/null
+/*
+ * UDI Driver helper macros
+ */
+#ifndef _UDI_HELPERS_H_
+#define _UDI_HELPERS_H_
+
+#if DEBUG_ENABLED
+# define DEBUG_OUT(fmt, v...) udi_debug_printf("%s: "fmt"\n", __func__ ,## v)
+#else
+# define DEBUG_OUT(...) do{}while(0)
+#endif
+
+#define ARRAY_COUNT(arr) (sizeof(arr)/sizeof(arr[0]))
+
+#define __EXPJOIN(a,b) a##b
+#define _EXPJOIN(a,b) __EXPJOIN(a,b)
+#define _EXPLODE(params...) params
+#define _ADDGCB(params...) (udi_cb_t *gcb, params)
+#define CONTIN(suffix, call, args, params) extern void _EXPJOIN(suffix##$,__LINE__) _ADDGCB params;\
+ call( _EXPJOIN(suffix##$,__LINE__), gcb, _EXPLODE args); } \
+ void _EXPJOIN(suffix##$,__LINE__) _ADDGCB params { \
+ rdata_t *rdata = gcb->context;
+
+/* Copied from http://projectudi.cvs.sourceforge.net/viewvc/projectudi/udiref/driver/udi_dpt/udi_dpt.h */
+#define UDIH_SET_ATTR_BOOLEAN(attr, name, val) \
+ udi_strcpy((attr)->attr_name, (name)); \
+ (attr)->attr_type = UDI_ATTR_BOOLEAN; \
+ (attr)->attr_length = sizeof(udi_boolean_t); \
+ UDI_ATTR32_SET((attr)->attr_value, (val))
+
+#define UDIH_SET_ATTR32(attr, name, val) \
+ udi_strcpy((attr)->attr_name, (name)); \
+ (attr)->attr_type = UDI_ATTR_UBIT32; \
+ (attr)->attr_length = sizeof(udi_ubit32_t); \
+ UDI_ATTR32_SET((attr)->attr_value, (val))
+
+#define UDIH_SET_ATTR_ARRAY8(attr, name, val, len) \
+ udi_strcpy((attr)->attr_name, (name)); \
+ (attr)->attr_type = UDI_ATTR_ARRAY8; \
+ (attr)->attr_length = (len); \
+ udi_memcpy((attr)->attr_value, (val), (len))
+
+#define UDIH_SET_ATTR_STRING(attr, name, val, len) \
+ udi_strcpy((attr)->attr_name, (name)); \
+ (attr)->attr_type = UDI_ATTR_STRING; \
+ (attr)->attr_length = (len); \
+ udi_strncpy_rtrim((char *)(attr)->attr_value, (val), (len))
+#define UDIH_SET_ATTR_STRFMT(attr, name, maxlen, fmt, v...) \
+ udi_strcpy((attr)->attr_name, (name)); \
+ (attr)->attr_type = UDI_ATTR_STRING; \
+ (attr)->attr_length = udi_snprintf((char *)(attr)->attr_value, (maxlen), (fmt) ,## v )
+
+/**
+ * \brief UDI PIO Helpers
+ */
+struct s_pio_ops {
+ udi_pio_trans_t *trans_list;
+ udi_ubit16_t list_length;
+ udi_ubit16_t pio_attributes;
+ udi_ubit32_t regset_idx;
+ udi_ubit32_t base_offset;
+ udi_ubit32_t length;
+};
+#define UDIH_PIO_OPS_ENTRY(list, attr, regset, base, len) {list, ARRAY_COUNT(list), attr, regset, base, len}
+#endif
--- /dev/null
+/*
+ * UDI Driver Helper Macros
+ *
+ * GFX-specific helpers
+ */
+#ifndef _HELPERS_GFX_H_
+#define _HELPERS_GFX_H_
+
+typedef struct {
+ udi_index_t op;
+ udi_ubit32_t arg_1;
+ udi_ubit32_t arg_2;
+ udi_ubit32_t arg_3;
+} gfxhelpers_op_t;
+
+typedef struct {
+ udi_index_t op_count;
+ const gfxhelpers_op_t *ops;
+} gfxhelpers_op_map_t;
+
+static inline udi_ubit32_t gfxhelpers_get_engine_op(
+ const gfxhelpers_op_map_t *map, udi_index_t index, udi_index_t prop
+ )
+{
+ if( index >= map->op_count ) {
+ return 0;
+ }
+ switch(prop) {
+ case UDI_GFX_PROP_OPERATOR_OPCODE: return map->ops[index].op;
+ case UDI_GFX_PROP_OPERATOR_ARG_1: return map->ops[index].arg_1;
+ case UDI_GFX_PROP_OPERATOR_ARG_2: return map->ops[index].arg_2;
+ case UDI_GFX_PROP_OPERATOR_ARG_3: return map->ops[index].arg_3;
+ }
+ return 0;
+}
+
+static inline void gfxhelpers_return_range_simple(
+ udi_gfx_range_connector_ack_op_t *callback, udi_gfx_range_cb_t *cb,
+ udi_ubit32_t min, udi_ubit32_t max, udi_ubit32_t step
+ )
+{
+
+}
+
+static inline void gfxhelpers_return_range_set(
+ udi_gfx_range_connector_ack_op_t *callback, udi_gfx_range_cb_t *cb,
+ udi_ubit32_t count, ...
+ )
+{
+
+}
+
+static inline void gfxhelpers_return_range_fixed(
+ udi_gfx_range_connector_ack_op_t *callback, udi_gfx_range_cb_t *cb,
+ udi_ubit32_t value
+ )
+{
+ gfxhelpers_return_range_simple(callback, cb, value, value, 1);
+}
+
+#endif
+
--- /dev/null
+
+
+- Indexed engine operator list (would be better as second set of calls)
+ > Arguments can be passed using custom attributes
+- GL-centric operations (would be better with read_buffer/write_buffer calls)
+- No status returned from operations most
+- Input data format specifcation
+ > Engine operator list specifies it, but in an abstract way (which would require quite smart code to determine)
+ > Doable though, but having a RO property on the engine that notes if it uses a standard pixel format would be nice
+
+
+Engine inspection API:
+> Readonly? Property for number of operations
+ - Readonly because modifcation of the count is not possible
+ - Attributes can be read, so that's your config mechanisim
+> udi_gfx_engine_getop_req(udi_gfx_state_cb_t *cb);
+> udi_gfx_engine_getop_ack(udi_gfx_state_cb_t *cb, udi_ubit8_t operation, udi_ubit32_t arg1, udi_ubit32_t arg2, udi_ubit32_t arg3);
+
+
+Buffer manipulation API:
+
+ struct {
+ udi_cb_t gcb;
+ udi_index_t buffer_idx;
+ udi_ubit32_t offset;
+ udi_buffer_t *data;
+ } udi_gfx_buffer_cb_t;
+
+> udi_gfx_buffer_write_req(udi_gfx_buffer_cb_t *cb);
+> udi_gfx_buffer_write_ack(udi_gfx_buffer_cb_t *cb, udi_status_t status);
+> (OPTIONAL) udi_gfx_buffer_read_req(udi_gfx_buffer_cb_t *cb);
#ifndef _UDI_H_
#define _UDI_H_
+#ifndef UDI_VERSION
+# error "Please define UDI_VERSION before including"
+#endif
+#if UDI_VERSION < 0x100
+# error "Requesting an unsupported UDI version (pre 1.0)"
+#endif
+#if UDI_VERSION > 0x101
+# error "Requesting an unsupported UDI version (post 1.01)"
+#endif
+
#include <stdint.h>
#include <stdarg.h>
--- /dev/null
+/**
+ * Summary: udi_gfx.h
+ * Contains the graphics metalanguage interface details
+ *
+ * Author:
+ * Marcel Sondaar
+ *
+ * License:
+ * <Public Domain>
+ *
+ * Source:
+ * https://www.d-rift.nl/combuster/mos3/?p=viewsource&file=/include/common/udi_gfx.h
+ */
+
+// note that the specification, and thus, the contents of this file is not fixed.
+
+#ifndef __UDI_GFX_H__
+#define __UDI_GFX_H__
+
+#include <udi.h>
+
+#ifndef UDI_GFX_VERSION
+#error "UDI_GFX_VERSION not defined."
+#elif UDI_GFX_VERSION != 0x101
+#error "UDI_GFX_VERSION not supported."
+#endif
+
+/**
+ * Enumeration: UDI_GFX_PROP
+ * Lists the various UDI properties
+ */
+
+// General state properties
+/* Constant: UDI_GFX_PROP_ENABLE
+ *
+ * Valid values:
+ * 0 - disabled
+ * 1 - enabled
+ *
+ * Ranges:
+ * Hardwired 1, or 0-1
+ *
+ * The connector or engine is enabled (nonzero) or disabled (zero). A disabled
+ * engine forwards all data from the previous stage unmodified. A disabled
+ * connector does not send any data over the connector. Drivers may power down
+ * the physical counterparts of disabled components to preserve power, and users
+ * should expect delays when enabling connectors or components representing
+ * framebuffers. Disabling is however not recommended for sprite layers, which
+ * may repeatedly be enabled and disabled. A disabled component can still have
+ * its state changed, and the driver must complete all commands and other state
+ * changes as expected, regardless of disabled or power state. The valid ranges
+ * reported for this property may be 1 (always enabled) or 0-1 (either enabled
+ * or disabled).
+ */
+#define UDI_GFX_PROP_ENABLE 0
+/* Constant: UDI_GFX_PROP_INPUT
+ *
+ * Valid values:
+ * Any valid engine ID, provided no dependency cycles are created, or -1
+ *
+ * Ranges:
+ * Any non-empty set of valid values. Often hardwired.
+ *
+ * Points to the engine that is processed before this unit. In the case of a
+ * connector, it points to the last engine in a pipeline, and each engine points
+ * to the next engine in the sequence. A value of -1 indicates a source that
+ * only yields black pixels. Implementations must not allow cyclic structures.
+ * Changing this value may reallocate resources, and engines that are no longer
+ * referenced may lose their data (but not their state) when it is not part of
+ * any pipeline. If preservation is required, the ENABLE state should be used
+ * instead. Valid ranges includes one or more from the list of engines and -1
+ * combined. In most cases, this property can not be modified.
+ */
+#define UDI_GFX_PROP_INPUT 1
+/* Constant: UDI_GFX_PROP_WIDTH
+ *
+ * Valid values:
+ * Any non-zero positive number.
+ *
+ * Ranges:
+ * Contains at least one valid value. Often only multiples of UNIT_WIDTH
+ * or a power of two are allowed. May be hardwired.
+ *
+ * Contains the amount of pixels in the horizontal direction. For connectors,
+ * this is the amount of data pixels rendered horizontally. For engines, this
+ * is the width in pixels of the image. Pixels requested from an engine outside
+ * the range (0..width-1) are defined according to the <UDI_GFX_PROP_CLIP>
+ * property. In some cases, hardware may support only fixed combinations of
+ * width and height. In such cases, changing the width will also change the
+ * height to a corresponding valid number. Valid ranges include any values
+ * strictly above zero. For connectors, expect large continuous ranges, large
+ * ranges with a certain modulus, a limited number of fixed values, or a
+ * constant value.
+ */
+#define UDI_GFX_PROP_WIDTH 2
+/* Constant: UDI_GFX_PROP_HEIGHT
+ *
+ * Valid values:
+ * Any non-zero positive number.
+ *
+ * Ranges:
+ * Contains at least one valid value. Often only multiples of UNIT_HEIGHT
+ * or a power of two are allowed. May be hardwired.
+ *
+ * Contains the amount of pixels in the vertical direction. Functions similar
+ * to the width property, but changing it will not alter the width property,
+ * and it's range at any time contains the valid range for the currently
+ * selected width.
+ */
+#define UDI_GFX_PROP_HEIGHT 3
+
+/* Constant: UDI_GFX_PROP_CUSTOM
+ * The first property index of the driver's custom range. These are not assigned
+ * directly assigned by the UDI specification, but may be specified in the
+ * operator tree.
+ */
+#define UDI_GFX_PROP_CUSTOM 1024
+
+// engine properties
+
+/* Constant: UDI_GFX_PROP_CLIP
+ *
+ * Valid values:
+ * 0 - points outside width x height are passed from next stage
+ * 1 - the engine's contents is tiled with size width x height
+ * 2 - points outside the width overflow into the y coordinate
+ * 3 - points outside the height overflow into the x coordinate
+ *
+ * Ranges:
+ * Hardwired zero for connectors. Any non-empty subset for engines, usually
+ * hardwired.
+ *
+ * For engines, contains the behaviour for pixels requested outside the width
+ * and height of the engine. Can be either 0 (pass from next stage), 1 (the
+ * coordinates are wrapped modulus the height and width), 2 (the coordinates
+ * overflow onto the next scanline horizontally, and wrap vertically), 3 (the
+ * coordinates overflow onto the next column vertically, and wrap horizontally).
+ * Valid ranges contain one or more of these options. For overlays and sprites,
+ * a value 0 is common. For framebuffers, 2 is the most common value. For
+ * connectors, this property is always 0 since they do not store pixel data
+ */
+#define UDI_GFX_PROP_CLIP 4
+
+/* Constant: UDI_GFX_PROP_UNIT_WIDTH
+ *
+ * Valid values:
+ * Any non-zero positive value
+ *
+ * Ranges:
+ * Any non-empty set of valid values. May be hardwired to 1 for
+ * framebuffers, or a range of small values for hardware scaling, or any
+ * larger hardwired number or set for tiling engines.
+ *
+ * Tiles are used to indicate that the hardware groups sets of pixels and have
+ * each group share certain properties, i.e. color or tile index, or share the
+ * same chroma subsample with only a different intensity. If the engine has no
+ * such grouping, or shares all properties over the entire contents, the value
+ * of this property should be 1. Some tile examples include rescaling, where a
+ * tile width of 2 indicates a pixel doubling in X direction, or in text mode
+ * where a tile width of 8 or 9 corresponds with the width of common bitmap
+ * fonts
+ */
+#define UDI_GFX_PROP_UNIT_WIDTH 5
+
+/* Constant: UDI_GFX_PROP_UNIT_HEIGHT
+ *
+ * Valid values:
+ * Any non-zero positive value
+ *
+ * Ranges:
+ * Any non-empty set of valid values. May be hardwired to 1 for
+ * framebuffers, or a range of small values for hardware scaling, or any
+ * larger hardwired number or set for tiling engines.
+ *
+ * See <UDI_GFX_PROP_UNIT_WIDTH>, but for the Y direction. Common values are
+ * 1-2 for framebuffers (doublescanning on or off), identical to the tile
+ * width, or mostly independent.
+ */
+#define UDI_GFX_PROP_UNIT_HEIGHT 6
+
+#define UDI_GFX_PROP_TRANSLATEX 7
+#define UDI_GFX_PROP_TRANSLATEY 8
+#define UDI_GFX_PROP_SOURCE_WIDTH 12
+#define UDI_GFX_PROP_SOURCE_HEIGHT 13
+#define UDI_GFX_PROP_GL_VERSION 14
+#define UDI_GFX_PROP_GLES_VERSION 15
+#define UDI_GFX_PROP_STATE_BLOCK 16
+/**
+ * Each engine consists of 1 or more operators
+ */
+#define UDI_GFX_PROP_OPERATOR_INDEX 17 //!< Index of operator to inspect/manipulate
+#define UDI_GFX_PROP_OPERATOR_OPCODE 18 //!< Operation performed by operator
+#define UDI_GFX_PROP_OPERATOR_ARG_1 19 //!< argument 1
+#define UDI_GFX_PROP_OPERATOR_ARG_2 20 //!< argument 2
+#define UDI_GFX_PROP_OPERATOR_ARG_3 21 //!< argument 3
+#define UDI_GFX_PROP_COLOR_BITS 22
+#define UDI_GFX_PROP_GL_TARGET 23
+#define UDI_GFX_PROP_INPUTX 25
+#define UDI_GFX_PROP_INPUTY 26
+
+// properties for removal:
+#define UDI_GFX_PROP_STORE_COUNT 24 // not generic
+#define UDI_GFX_PROP_STORE_WIDTH 9 // not generic
+#define UDI_GFX_PROP_STORE_HEIGHT 10 // not generic
+#define UDI_GFX_PROP_STORE_BITS 11 // not generic
+#define UDI_GFX_PROP_PALETTE 1024 // optional, can be derived from the operator tree
+#define UDI_GFX_PROP_BUFFER 1025 // optional, can be derived from the operator tree
+#define UDI_GFX_PROP_TILESHEET 1026 // optional, can be derived from the operator tree
+
+// connector properties
+#define UDI_GFX_PROP_SIGNAL 23
+#define UDI_GFX_PROP_CONNECTOR_TYPE 24
+#define UDI_GFX_PROP_VGA_H_FRONT_PORCH 25
+#define UDI_GFX_PROP_VGA_H_BACK_PORCH 26
+#define UDI_GFX_PROP_VGA_H_SYNC 27
+#define UDI_GFX_PROP_VGA_V_FRONT_PORCH 28
+#define UDI_GFX_PROP_VGA_V_BACK_PORCH 29
+#define UDI_GFX_PROP_VGA_V_SYNC 30
+#define UDI_GFX_PROP_DOT_CLOCK 31
+#define UDI_GFX_PROP_VGA_H_SYNC_POL 32
+#define UDI_GFX_PROP_VGA_V_SYNC_POL 33
+
+/**
+ * Enumeration: UDI_GFX_SIGNAL
+ * Lists the various signal types
+ */
+#define UDI_GFX_SIGNAL_HIDDEN 0
+#define UDI_GFX_SIGNAL_INTEGRATED 0
+#define UDI_GFX_SIGNAL_RGBHV 1
+#define UDI_GFX_SIGNAL_RGBS 2
+#define UDI_GFX_SIGNAL_RGSB 3
+#define UDI_GFX_SIGNAL_YPBPR 4
+#define UDI_GFX_SIGNAL_DVID 5
+#define UDI_GFX_SIGNAL_YUV 6
+#define UDI_GFX_SIGNAL_YIQ 7
+#define UDI_GFX_SIGNAL_Y_UV 8
+#define UDI_GFX_SIGNAL_Y_IQ 9
+#define UDI_GFX_SIGNAL_HDMI 10
+#define UDI_GFX_SIGNAL_TEXT 11
+#define UDI_GFX_SIGNAL_CUSTOM 12
+
+/**
+ * Enumeration: UDI_GFX_CONNECTOR
+ * Lists the various external connectors
+ */
+#define UDI_GFX_CONNECTOR_HIDDEN 0
+#define UDI_GFX_CONNECTOR_VGA 1
+#define UDI_GFX_CONNECTOR_DVI 2
+#define UDI_GFX_CONNECTOR_SVIDEO 3
+#define UDI_GFX_CONNECTOR_COMPONENT 4
+#define UDI_GFX_CONNECTOR_HDMI 5
+#define UDI_GFX_CONNECTOR_RF 6
+#define UDI_GFX_CONNECTOR_SCART 7
+#define UDI_GFX_CONNECTOR_COMPOSITE 8
+
+/**
+ * Enumeration: UDI_GFX_OPERATOR
+ * Lists the display output operator
+ *
+ * a1/a2/a3 represents taking the output of a previous operation
+ * v1/v2/v3 represents the literal value of that argument
+ */
+#define UDI_GFX_OPERATOR_RGB 0 // output = (color) red(a1) + green(a2) + blue(a3) (each component is UDI_GFX_PROP_COLOR_BITS
+#define UDI_GFX_OPERATOR_YUV 1 // output = (color) Y(a1) + U(a2) + V(a3)
+#define UDI_GFX_OPERATOR_YIQ 2 // output = (color) Y(a1) + I(a2) + Q(a3)
+#define UDI_GFX_OPERATOR_I 3 // output = (color) intensity(a1)
+#define UDI_GFX_OPERATOR_ALPHA 4 // output = (color) a1 + alpha(a2)
+#define UDI_GFX_OPERATOR_ADD 5 // output = a1 + a2 + v3
+#define UDI_GFX_OPERATOR_SUB 6 // output = a1 - a2 - v3
+#define UDI_GFX_OPERATOR_MUL 7 // output = a1 * a2
+#define UDI_GFX_OPERATOR_DIV 8 // output = a1 / a2
+#define UDI_GFX_OPERATOR_MAD 9 // output = a1 * a2 + a3
+#define UDI_GFX_OPERATOR_FRC 10 // output = (a1 * a2) / a3
+#define UDI_GFX_OPERATOR_SHR 11 // output = a1 >> (a2 + v3)
+#define UDI_GFX_OPERATOR_SHL 12 // output = a1 << (a2 + v3)
+#define UDI_GFX_OPERATOR_ROR 13 // output = a1 >> a2 (over a3 bits)
+#define UDI_GFX_OPERATOR_ROL 14 // output = a1 << a2 (over a3 bits)
+#define UDI_GFX_OPERATOR_SAR 15 // output = a1 >> a2 (width is a3 bits, i.e. empties are filled with bit a3-1)
+#define UDI_GFX_OPERATOR_SAL 16 // output = a1 <<< (a2 + v3) (empties filled with bit 0)
+#define UDI_GFX_OPERATOR_AND 17 // output = a1 & a2
+#define UDI_GFX_OPERATOR_OR 18 // output = a1 | a2 | v3
+#define UDI_GFX_OPERATOR_NOT 19 // output = ~a1
+#define UDI_GFX_OPERATOR_XOR 20 // output = a1 ^ a2 ^ v3
+#define UDI_GFX_OPERATOR_NEG 21 // output = -a1
+#define UDI_GFX_OPERATOR_SEG 22 // output = (a1 >> v2) & (2**v3-1) (select v3 bits starting from bit v2)
+#define UDI_GFX_OPERATOR_RANGE 23 // output = (a1 > a2) ? a2 : ((a1 < a3) ? a3 : a1)
+#define UDI_GFX_OPERATOR_CONST 24 // output = v1
+#define UDI_GFX_OPERATOR_ATTR 25 // output = property[a1 + v2]
+#define UDI_GFX_OPERATOR_SWITCH 26 // output = output[(a1 % v3) + v2]
+#define UDI_GFX_OPERATOR_BUFFER 27 // output = buffer[a1][a2] (buffer is v3 bits per entry)
+#define UDI_GFX_OPERATOR_X 28 // output = output x pixel
+#define UDI_GFX_OPERATOR_Y 29 // output = output y pixel
+#define UDI_GFX_OPERATOR_TX 30 // output = horizontal tile index belonging to output pixel
+#define UDI_GFX_OPERATOR_TY 31 // output = vertical tile index belonging to output pixel
+#define UDI_GFX_OPERATOR_TXOFF 32 // output = horizontal offset from start of tile
+#define UDI_GFX_OPERATOR_TYOFF 33 // output = vertical offset from start of tile
+#define UDI_GFX_OPERATOR_INPUT 34 // output = input engine[x][y] component v1
+#define UDI_GFX_OPERATOR_DINPUT 35 // output = input engine[a1][a2] component v3
+
+
+
+// Constant: UDI_GFX_PROVIDER_OPS_NUM
+// the ops number used for the graphics driver
+#define UDI_GFX_PROVIDER_OPS_NUM 1
+
+// Constant: UDI_GFX_CLIENT_OPS_NUM
+// the ops number used for the graphics application
+#define UDI_GFX_CLIENT_OPS_NUM 2
+
+// Structure: udi_gfx_bind_cb_t
+// Contains the operations of a driver binding request
+typedef struct {
+ // Variable: gcb
+ // The main control block
+ udi_cb_t gcb;
+} udi_gfx_bind_cb_t;
+#define UDI_GFX_BIND_CB_NUM 1
+
+// Function: udi_block_bind_req
+// function pointer prototype for connecting to a block device
+//
+// in:
+// cb - A pointer to a <udi_block_bind_cb_t>
+//
+typedef void udi_gfx_bind_req_op_t (udi_gfx_bind_cb_t *cb );
+udi_gfx_bind_req_op_t udi_gfx_bind_req;
+
+// Function: udi_gfx_bind_ack
+// function pointer prototype for acknowledging a connection request
+//
+// in:
+// cb - A pointer to a <udi_gfx_bind_cb_t>
+// sockets - The number of addressable socket components
+// engines - The number of addressable engine components
+// status - The result of the bind operation
+//
+typedef void udi_gfx_bind_ack_op_t (udi_gfx_bind_cb_t *cb, udi_index_t sockets, udi_index_t engines, udi_status_t status );
+udi_gfx_bind_ack_op_t udi_gfx_bind_ack;
+
+// Function: udi_gfx_unbind_req
+// function pointer prototype for disconnecting a block device
+//
+// in:
+// cb - A pointer to a <udi_block_bind_cb_t>
+//
+typedef void udi_gfx_unbind_req_op_t (udi_gfx_bind_cb_t *cb );
+udi_gfx_unbind_req_op_t udi_gfx_unbind_req;
+
+// Function: udi_gfx_unbind_ack
+// function pointer prototype for connecting to a block device
+//
+// in:
+// cb - A pointer to a <udi_gfx_bind_cb_t>
+//
+typedef void udi_gfx_unbind_ack_op_t (udi_gfx_bind_cb_t *cb );
+udi_gfx_unbind_ack_op_t udi_gfx_unbind_ack;
+
+// Structure: udi_gfx_state_cb_t
+// Contains the operations of a read/write transaction
+typedef struct {
+ // Variable: gcb
+ // The main control block
+ udi_cb_t gcb;
+ udi_ubit32_t subsystem;
+ udi_ubit32_t attribute;
+} udi_gfx_state_cb_t;
+#define UDI_GFX_STATE_CB_NUM 2
+
+// Function: udi_gfx_set_engine_req
+// function pointer prototype for setting an engine state
+//
+// in:
+// cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_set_engine_req_op_t (udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+udi_gfx_set_engine_req_op_t udi_gfx_set_engine_req;
+
+// Function: udi_gfx_set_connector_req
+// function pointer prototype for setting an connector state
+//
+// in:
+// cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_set_connector_req_op_t (udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+udi_gfx_set_connector_req_op_t udi_gfx_set_connector_req;
+
+// Function: udi_gfx_set_engine_ack
+// function pointer prototype for setting an engine state
+//
+// in:
+// cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_set_engine_ack_op_t (udi_gfx_state_cb_t *cb );
+udi_gfx_set_engine_ack_op_t udi_gfx_set_engine_ack;
+
+// Function: udi_gfx_set_connector_ack
+// function pointer prototype for setting an engine state
+//
+// in:
+// cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_set_connector_ack_op_t (udi_gfx_state_cb_t *cb );
+udi_gfx_set_connector_ack_op_t udi_gfx_set_connector_ack;
+
+// Function: udi_gfx_get_engine_req
+// function pointer prototype for setting an engine state
+//
+// in:
+// cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_get_engine_req_op_t (udi_gfx_state_cb_t *cb );
+udi_gfx_get_engine_req_op_t udi_gfx_get_engine_req;
+
+// Function: udi_gfx_get_connector_req
+// function pointer prototype for setting an connector state
+//
+// in:
+// cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_get_connector_req_op_t (udi_gfx_state_cb_t *cb );
+udi_gfx_get_connector_req_op_t udi_gfx_get_connector_req;
+
+// Function: udi_gfx_get_engine_ack
+// function pointer prototype for setting an engine state
+//
+// in:
+// cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_get_engine_ack_op_t (udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+udi_gfx_get_engine_ack_op_t udi_gfx_get_engine_ack;
+
+// Function: udi_gfx_get_connector_ack
+// function pointer prototype for setting an engine state
+//
+// in:
+// cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_get_connector_ack_op_t (udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+udi_gfx_get_connector_ack_op_t udi_gfx_get_connector_ack;
+
+
+// Structure: udi_gfx_range_cb_t
+// Contains the operations of a range request transaction
+typedef struct {
+ // Variable: gcb
+ // The main control block
+ udi_cb_t gcb;
+ udi_ubit32_t subsystem;
+ udi_ubit32_t attribute;
+ udi_buf_t * rangedata;
+} udi_gfx_range_cb_t;
+#define UDI_GFX_RANGE_CB_NUM 3
+
+// Function: udi_gfx_range_engine_req
+// function pointer prototype for getting an engine property range
+//
+// in:
+// cb - A pointer to a <udi_gfx_range_cb_t>
+//
+typedef void udi_gfx_range_engine_req_op_t (udi_gfx_range_cb_t *cb );
+udi_gfx_range_engine_req_op_t udi_gfx_range_engine_req;
+
+// Function: udi_gfx_range_connector_req
+// function pointer prototype for getting a connector property range
+//
+// in:
+// cb - A pointer to a <udi_gfx_range_cb_t>
+//
+typedef void udi_gfx_range_connector_req_op_t (udi_gfx_range_cb_t *cb );
+udi_gfx_range_connector_req_op_t udi_gfx_range_connector_req;
+
+// Function: udi_gfx_range_engine_ack
+// function pointer prototype for replying an engine property range
+//
+// in:
+// cb - A pointer to a <udi_gfx_range_cb_t>
+//
+typedef void udi_gfx_range_engine_ack_op_t (udi_gfx_range_cb_t *cb );
+udi_gfx_range_engine_ack_op_t udi_gfx_range_engine_ack;
+
+// Function: udi_gfx_range_connector_ack
+// function pointer prototype for replying a connector property range
+//
+// in:
+// cb - A pointer to a <udi_gfx_range_cb_t>
+//
+typedef void udi_gfx_range_connector_ack_op_t (udi_gfx_range_cb_t *cb );
+udi_gfx_range_connector_ack_op_t udi_gfx_range_connector_ack;
+
+
+// Structure: udi_gfx_command_cb_t
+// Contains the operations of a command sequence
+typedef struct {
+ // Variable: gcb
+ // The main control block
+ udi_cb_t gcb;
+ udi_buf_t * commanddata;
+} udi_gfx_command_cb_t;
+#define UDI_GFX_COMMAND_CB_NUM 4
+
+// Function: udi_gfx_command
+// function pointer prototype for sending command data
+//
+// in:
+// cb - A pointer to a <udi_gfx_command_cb_t>
+//
+typedef void udi_gfx_command_req_op_t (udi_gfx_command_cb_t *cb );
+udi_gfx_command_req_op_t udi_gfx_command_req;
+
+// Function: udi_gfx_command_ack
+// function pointer prototype for sending command data
+//
+// in:
+// cb - A pointer to a <udi_gfx_command_cb_t>
+//
+typedef void udi_gfx_command_ack_op_t (udi_gfx_command_cb_t *cb);
+udi_gfx_command_ack_op_t udi_gfx_command_ack;
+
+/* Structure: udi_gfx_provider_ops_t
+ The graphics metalanguage e*ntry points (provider side)
+ */
+typedef const struct {
+ udi_channel_event_ind_op_t *channel_event_ind_op;
+ udi_gfx_bind_req_op_t *gfx_bind_req_op;
+ udi_gfx_unbind_req_op_t *gfx_unbind_req_op;
+ udi_gfx_set_connector_req_op_t *gfx_set_connector_req_op;
+ udi_gfx_set_engine_req_op_t *gfx_set_engine_req_op;
+ udi_gfx_get_connector_req_op_t *gfx_get_connector_req_op;
+ udi_gfx_get_engine_req_op_t *gfx_get_engine_req_op;
+ udi_gfx_range_connector_req_op_t *gfx_range_connector_req_op;
+ udi_gfx_range_engine_req_op_t *gfx_range_engine_req_op;
+ udi_gfx_command_req_op_t *gfx_command_op;
+} udi_gfx_provider_ops_t;
+
+/* Structure: udi_gfx_client_ops_t
+ * The graphics metalanguage entry points (client side)
+ */
+typedef const struct {
+ udi_channel_event_ind_op_t *channel_event_ind_op;
+ udi_gfx_bind_ack_op_t *udi_gfx_bind_ack;
+ udi_gfx_unbind_ack_op_t *udi_gfx_unbind_ack;
+ udi_gfx_set_connector_ack_op_t *udi_gfx_set_connector_ack;
+ udi_gfx_set_engine_ack_op_t *udi_gfx_set_engine_ack;
+ udi_gfx_get_connector_ack_op_t *udi_gfx_get_connector_ack;
+ udi_gfx_get_engine_ack_op_t *udi_gfx_get_engine_ack;
+ udi_gfx_range_connector_ack_op_t *udi_gfx_range_connector_ack;
+ udi_gfx_range_engine_ack_op_t *udi_gfx_range_engine_ack;
+ udi_gfx_command_ack_op_t *udi_gfx_command_ack;
+} udi_gfx_client_ops_t;
+
+
+// temporary
+#ifndef UDI_ANNOY_ME
+void EngineReturnSimpleRange (int source, int index, int prop, int first, int last, int modulus);
+void ConnectorReturnSimpleRange (int source, int index, int prop, int first, int last, int modulus);
+void EngineReturnConstantRange (int source, int index, int prop, int value);
+void ConnectorReturnConstantRange (int source, int index, int prop, int value);
+void EngineReturnBooleanRange (int source, int index, int prop, int value1, int value2);
+void ConnectorReturnBooleanRange (int source, int index, int prop, int value1, int value2);
+#endif
+
+#endif
+
--- /dev/null
+Layers:
+
+IPC / Client management
+Compositor / Window Manager
+Renderer / Window Contents
+
+Renderers
+Window Management
+> "WM_CreateWindow(Parent, Class, Name)"
+Window Drawing
+> "WD_Fill"
+> "WD_Blit"
+> "WD_LockSurface"
+> "WD_UnlockSurface"
+Decorations
+> ".InitWindow"
+> ".Render"
++ "WM_SetBorder"
+Compositing
+> Dirty rectangling, use 2DCmd to selectively blit
+> Request kernel/server buffers if possible
+
+
+Clients own windows
+Windows are composed of multiple regions that conform to several types (see below)
+- Re-draw is handled by using these regions
+
+Server-side rendering primitives:
+ # Apply to regions, rendered in fixed order, each has an ID
+> Auto-scaling bitmaps
+ - Control backed by an image with three five regions per axis
+ Edge Fixed, Fill, Center Fixed, Fill, Edge Fixed
+ - Definition is via two pixel counts (edge width, fill width), rest is derived
+ - Command to switch backing image to another already provided
+> Tiling bitmaps + filled rects
+> Text (single line)
+> Canvas (Takes drawing commands, draws to internal buffer)
+> Shared buffer (of an unspecified pixel format)
+
+=== Config options ===
+- Root App
+- Display device (- = stdout)
+- Keyboard device (- = stdin)
+- Mouse device
+- Pipe suffix, port number, etc.
+- Key bindings
+
--- /dev/null
+/*
+ *
+ */
+#include <IWindow.hpp>
+
+namespace AxWin {
+
+
+IWindow::IWindow(const std::string &name):
+ m_name(name)
+{
+}
+
+} // namespace AxWin
+
--- /dev/null
+
+include ../../Makefile.cfg
+
+CPPFLAGS += -Iinclude/
+OBJ := IWindow.o
+BIN := AxWinServer
+
+include ../../Makefile.tpl
--- /dev/null
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * compositor.cpp
+ * - Window compositor
+ */
+#include <CVideo.hpp>
+#include <CCompositor.hpp>
+
+namespace AxWin {
+
+CCompositor* CCompositor::s_instance;
+
+void CCompositor::Initialise(const CConfigCompositor& config)
+{
+ assert(!CCompositor::s_instance);
+ CCompositor::s_instance = new CCompositor(config);
+}
+
+CCompositor::CCompositor(const CConfigCompositor& config):
+ m_config(config)
+{
+ //
+}
+
+IWindow* CCompositor::CreateWindow(CClient& client)
+{
+ return new CWindow(client);
+}
+
+void CCompositor::Redraw()
+{
+ // Redraw the screen and clear damage rects
+ if( m_damageRects.empty() )
+ return ;
+
+ // For all windows, check for intersection with damage rect
+}
+
+void CCompositor::DamageArea(const Rect& area)
+{
+
+}
+
+} // namespace AxWin
+
--- /dev/null
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * CCompositor.hpp
+ * - Window Compositor
+ */
+#ifndef _CCOMPOSITOR_H_
+#define _CCOMPOSITOR_H_
+
+
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * CConfig.hpp
+ * - Configuration class
+ */
+#ifndef _CCONFIG_H_
+#define _CCONFIG_H_
+
+#include "CConfigInput.hpp"
+#include "CConfigVideo.hpp"
+#include "CConfigIPC.hpp"
+
+namespace AxWin {
+
+class CConfig
+{
+public:
+ CConfig();
+
+ bool parseCommandline(int argc, char *argv[]);
+
+ CConfigInput m_input;
+ CConfigVideo m_video;
+ CConfigIPC m_ipc;
+};
+
+}
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * IRegion.hpp
+ * - Representation of a primitive region in a window (part of the window render list)
+ */
+#ifndef _IREGION_HPP_
+#define _IREGION_HPP_
+
+#include <string>
+#include <vector>
+#include "CRect.hpp"
+
+namespace AxWin {
+
+class IRegion
+{
+protected:
+ CWindow& m_parentWindow;
+public:
+ virtual IRegion(CWindow& parent, const ::AxWin::Rect& position);
+ virtual ~IRegion();
+
+ virtual void Redraw(const ::AxWin::Rect& area) = 0;
+ virtual bool SetAttr(unsigned int Index, const IPCAttrib& Value) = 0;
+};
+
+};
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * IVideo.hpp
+ * - Graphics backend abstraction
+ */
+#ifndef _IVIDEO_HPP_
+#define _IVIDEO_HPP_
+
+namespace AxWin {
+
+class IVideo
+{
+public:
+ virtual ~IVideo();
+
+ // Allocate a new hardware surface
+ IHWSurface& AllocateHWSurface(uint16_t Width, uint16_t Height);
+
+ // Request redraw of backbuffer
+ void Flip();
+};
+
+};
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * IWindow.hpp
+ * - Window abstract base class
+ */
+#ifndef _IWINDOW_HPP_
+#define _IWINDOW_HPP_
+
+#include <string>
+#include <vector>
+#include "CRect.hpp"
+
+namespace AxWin {
+
+class IWindow
+{
+public:
+ virtual IWindow(const ::std::string &name);
+ virtual ~IWindow();
+
+ virtual void Repaint() = 0;
+
+ virtual void MouseButton(int ButtonID, int X, int Y, bool Down);
+ virtual void MouseMove(int NewX, int NewY);
+ virtual void KeyEvent(uint32_t Scancode, const ::std::string &Translated, bool Down);
+protected:
+ const ::std::string m_name;
+};
+
+} // namespace AxWin
+
+#endif
+
--- /dev/null
+/*
+ */
+#include <CConfig.hpp>
+#include <ipc.hpp>
+#include <input.hpp>
+#include <video.hpp>
+#include <timing.hpp>
+
+using namespace AxWin;
+
+// === CODE ===
+int main(int argc, char *argv[])
+{
+ // - Load configuration (from file and argv)
+ CConfig config;
+ try {
+ config.parseCommandline(argc, argv);
+ }
+ catch(const std::exception& e) {
+ fprintf(stderr, "Exception: %s\n", e.what());
+ return 1;
+ }
+ // - Open graphics
+ Graphics::Initialise(config.m_video);
+ // - Open input
+ Input::Initialise(config.m_input);
+ // > Handles hotkeys?
+ // - Initialise compositor structures
+ Compositor::Initialise(config.m_compositor);
+ // - Bind IPC channels
+ IPC::Initialise(config.m_ipc);
+ // - Start root child process (from config)
+ // TODO: Spin up child process
+
+ // - Event loop
+ for( ;; )
+ {
+ int nfd = 0;
+ fd_set rfds;
+
+ Input::FillSelect(&nfd, &rfds);
+ IPC::FillSelect(&nfd, &rfds);
+
+ // TODO: Timer events
+ int64_t timeout = Timing::GetTimeToNextEvent();
+ int rv = ::_SysSelect(nfd, &rfds, NULL, &rfds, NULL, 0);
+
+ Timing::CheckEvents();
+
+ Input::HandleSelect(&rfds);
+ IPC::HandleSelect(&rfds);
+
+ Compositor::Redraw();
+ }
+ return 0;
+}
+
switch( *Buf )
{
+ case '\a':
+ // Alarm, aka bell
+ //Display_SoundBell(Term);
+ break;
case '\b':
// backspace is aprarently just supposed to cursor left (if possible)
Display_MoveCursor(Term, 0, -1);
// Reset
Display_ResetAttributes(Term);
}
+ else if( args[0] == 48 )
+ {
+ // ISO-8613-3 Background
+ if( args[1] == 2 ) {
+ uint32_t col = 0;
+ col |= (uint32_t)args[2] << 16;
+ col |= (uint32_t)args[3] << 8;
+ col |= (uint32_t)args[4] << 0;
+ Display_SetBackground(Term, col);
+ }
+ else if( args[1] == 5 ) {
+ _SysDebug("TODO: Support xterm palette BG %i", args[2]);
+ }
+ else {
+ _SysDebug("VT100 Unknown mode set \e[48;%im", args[1]);
+ }
+ }
+ else if( args[0] == 38 )
+ {
+ // ISO-8613-3 Foreground
+ if( args[1] == 2 ) {
+ uint32_t col = 0;
+ col |= (uint32_t)args[2] << 16;
+ col |= (uint32_t)args[3] << 8;
+ col |= (uint32_t)args[4] << 0;
+ Display_SetForeground(Term, col);
+ }
+ else if( args[1] == 5 ) {
+ _SysDebug("TODO: Support xterm palette FG %i", args[2]);
+ }
+ else {
+ _SysDebug("VT100 Unknown mode set \e[38;%im", args[1]);
+ }
+ }
else
{
for( int i = 0; i < argc; i ++ )
case 4:
_SysDebug("TODO: \\e[4m - Underscore");
break;
+ //case 5:
+ // _SysDebug("TODO: \\e[5m - Blink/bold");
+ // break;
case 7:
_SysDebug("TODO: \\e[7m - Reverse");
break;
st->CurBG = 0;
Display_SetBackground( Term, caVT100Colours[ st->CurBG ] );
break;
+ case 90 ... 97:
+ st->CurFG = args[i]-90 + 8;
+ Display_SetForeground( Term, caVT100Colours[ st->CurBG ] );
+ break;;
+ case 100 ... 107:
+ st->CurBG = args[i]-100 + 8;
+ Display_SetBackground( Term, caVT100Colours[ st->CurBG ] );
+ break;;
default:
_SysDebug("Unknown mode set \\e[%im", args[i]);
break;
char buffer[101];
size_t len = snprintf(buffer, 100, "[%lli] init spawning ", _SysTimestamp());
_SysWrite(out, buffer, len);
- char ch = '\'';
for( int i = 0; Program->Command[i]; i ++ )
{
- _SysWrite(out, &ch, 1);
+ _SysWrite(out, "'", 1);
_SysWrite(out, Program->Command[i], strlen(Program->Command[i]));
- _SysWrite(out, &ch, 1);
+ _SysWrite(out, "'", 1);
}
- ch = '\n';
- _SysWrite(out, &ch, 1);
+ _SysWrite(out, "\n", 1);
}
return SpawnCommand(in, out, err, Program->Command, NULL);
LDFLAGS += -lnet -lreadline
-OBJ = main.o
+OBJ = main.o server.o input.o
+OBJ += window.o pseudo_curses.o
BIN = irc
-include ../Makefile.tpl
--- /dev/null
+/*
+ */
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+#include "pseudo_curses.h"
+
+typedef struct sServer tServer;
+
+extern void _SysDebug(const char *format, ...);
+
+extern int writef(int FD, const char *Format, ...);
+extern int OpenTCP(const char *AddressString, short PortNumber);
+extern char *GetValue(char *Src, int *Ofs);
+
+extern void Redraw_Screen(void);
+extern void Exit(const char *Reason) __attribute__((noreturn));
+
+#endif
+
--- /dev/null
+/*
+ */
+#include "input.h"
+#include "window.h"
+#include "server.h"
+#include <readline.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+// === PROTOTYPES ===
+void Input_FillSelect(int *nfds, fd_set *rfds);
+void Input_HandleSelect(int nfds, const fd_set *rfds);
+ int ParseUserCommand(char *String);
+
+// === GLOBALS ===
+tReadline *gpInput_ReadlineInfo;
+
+// === CODE ===
+void Input_FillSelect(int *nfds, fd_set *rfds)
+{
+ if( !gpInput_ReadlineInfo ) {
+ gpInput_ReadlineInfo = Readline_Init(1);
+ }
+
+ FD_SET(0, rfds);
+ if(*nfds < 0+1)
+ *nfds = 0+1;
+}
+
+void Input_HandleSelect(int nfds, const fd_set *rfds)
+{
+ // User input
+ if(FD_ISSET(0, rfds))
+ {
+ char *cmd = Readline_NonBlock(gpInput_ReadlineInfo);
+ if( cmd )
+ {
+ if( cmd[0] )
+ {
+ ParseUserCommand(cmd);
+ }
+ free(cmd);
+ // Prompt
+ SetCursorPos(giTerminal_Height-1, 1);
+ printf("\x1B[2K"); // Clear line
+ printf("[%s]", Window_GetName(NULL));
+ }
+ }
+}
+
+void Cmd_join(char *ArgString)
+{
+ int pos=0;
+ char *channel_name = GetValue(ArgString, &pos);
+
+ tServer *srv = Window_GetServer(NULL);
+
+ if( srv )
+ {
+ Windows_SwitchTo( Window_Create(srv, channel_name) );
+ Redraw_Screen();
+ Server_SendCommand(srv, "JOIN :%s", channel_name);
+ }
+}
+
+void Cmd_quit(char *ArgString)
+{
+ const char *quit_message = ArgString;
+ if( quit_message == NULL || quit_message[0] == '\0' )
+ quit_message = "/quit - Acess2 IRC Client";
+
+ Servers_CloseAll(quit_message);
+
+ Exit(NULL); // NULL = user requested
+}
+
+void Cmd_window(char *ArgString)
+{
+ int pos = 0;
+ char *window_id = GetValue(ArgString, &pos);
+ int window_num = atoi(window_id);
+
+ if( window_num > 0 )
+ {
+ // Get `window_num`th window
+ tWindow *win = Windows_GetByIndex(window_num-1);
+ if( win )
+ {
+ Windows_SwitchTo( win );
+ }
+ else
+ {
+ // Otherwise, silently ignore
+ }
+ }
+ else
+ {
+ window_num = 1;
+ for( tWindow *win; (win = Windows_GetByIndex(window_num-1)); window_num ++ )
+ {
+ Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, NULL, "%i: %s", window_num, Window_GetName(win));
+ }
+ }
+}
+
+const struct {
+ const char *Name;
+ void (*Fcn)(char *ArgString);
+} caCommands[] = {
+ {"join", Cmd_join},
+ {"quit", Cmd_quit},
+ {"window", Cmd_window},
+ {"win", Cmd_window},
+ {"w", Cmd_window},
+};
+const int ciNumCommands = sizeof(caCommands)/sizeof(caCommands[0]);
+
+/**
+ * \brief Handle a line from the prompt
+ */
+int ParseUserCommand(char *String)
+{
+ if( String[0] == '/' )
+ {
+ char *command;
+ int pos = 0;
+
+ command = GetValue(String, &pos)+1;
+
+ // TODO: Prefix matches
+ int cmdIdx = -1;
+ for( int i = 0; i < ciNumCommands; i ++ )
+ {
+ if( strcmp(command, caCommands[i].Name) == 0 ) {
+ cmdIdx = i;
+ break;
+ }
+ }
+ if( cmdIdx != -1 ) {
+ caCommands[cmdIdx].Fcn(String+pos);
+ }
+ else
+ {
+ Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, NULL, "Unknown command %s", command);
+ }
+ }
+ else
+ {
+ // Message
+ // - Only send if server is valid and window name is non-empty
+ tServer *srv = Window_GetServer(NULL);
+ if( srv && Window_IsChat(NULL) ) {
+ Window_AppendMessage(NULL, MSG_CLASS_MESSAGE, Server_GetNick(srv), "%s", String);
+ Server_SendCommand(srv, "PRIVMSG %s :%s\n", Window_GetName(NULL), String);
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ */
+#ifndef _INPUT_H_
+#define _INPUT_H_
+
+#include <acess/sys.h>
+
+extern void Input_FillSelect(int *nfds, fd_set *rfds);
+extern void Input_HandleSelect(int nfds, const fd_set *rfds);
+
+#endif
+
#include <stdio.h>
#include <string.h>
#include <net.h>
-#include <readline.h>
-#include <acess/devices/pty.h>
#include <stdarg.h>
-// === TYPES ===
-typedef struct sServer {
- struct sServer *Next;
- int FD;
- char InBuf[BUFSIZ+1];
- int ReadPos;
- char Name[];
-} tServer;
-
-typedef struct sMessage
-{
- struct sMessage *Next;
- time_t Timestamp;
- tServer *Server;
- int Type;
- char *Source; // Pointer into `Data`
- char Data[];
-} tMessage;
+#include "common.h"
+#include "input.h"
+#include "window.h"
+#include "server.h"
-typedef struct sWindow
-{
- struct sWindow *Next;
- tMessage *Messages;
- tServer *Server; //!< Canonical server (can be NULL)
- int ActivityLevel;
- char Name[]; // Channel name / remote user
-} tWindow;
-
-enum eMessageTypes
-{
- MSG_TYPE_NULL,
- MSG_TYPE_SERVER, // Server message
-
- MSG_TYPE_NOTICE, // NOTICE command
- MSG_TYPE_JOIN, // JOIN command
- MSG_TYPE_PART, // PART command
- MSG_TYPE_QUIT, // QUIT command
-
- MSG_TYPE_STANDARD, // Standard line
- MSG_TYPE_ACTION, // /me
-
- MSG_TYPE_UNK
-};
+// === TYPES ===
// === PROTOTYPES ===
int main(int argc, const char *argv[], const char *envp[]);
int MainLoop(void);
int ParseArguments(int argc, const char *argv[]);
- int ParseUserCommand(char *String);
// ---
-tServer *Server_Connect(const char *Name, const char *AddressString, short PortNumber);
-tMessage *Message_AppendF(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message, ...) __attribute__((format(__printf__,5,6)));
-tMessage *Message_Append(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message);
-tWindow *Window_Create(tServer *Server, const char *Name);
void Redraw_Screen(void);
-
- int ProcessIncoming(tServer *Server);
// --- Helpers
-void SetCursorPos(int Row, int Col);
+void Exit(const char *Reason);
int writef(int FD, const char *Format, ...);
int OpenTCP(const char *AddressString, short PortNumber);
char *GetValue(char *Str, int *Ofs);
-static inline int isdigit(int ch);
// === GLOBALS ===
-char *gsUsername = "user";
-char *gsHostname = "acess";
-char *gsRealName = "Acess2 IRC Client";
-char *gsNickname = "acess";
-tServer *gpServers;
-tWindow gWindow_Status = {
- NULL, NULL, NULL, // No next, empty list, no server
- 0, {""} // No activity, empty name (rendered as status)
-};
-tWindow *gpWindows = &gWindow_Status;
-tWindow *gpCurrentWindow = &gWindow_Status;
- int giTerminal_Width = 80;
- int giTerminal_Height = 25;
+const char *gsExitReason = "No reason [BUG]";
// ==== CODE ====
void ExitHandler(void)
{
printf("\x1B[?1047l");
- printf("Quit\n");
+ printf("Quit: %s\n", gsExitReason);
+}
+
+void Exit(const char *Reason)
+{
+ gsExitReason = (Reason ? Reason : "User Requested");
+ exit( (Reason ? 1 : 0) );
}
int main(int argc, const char *argv[], const char *envp[])
atexit(ExitHandler);
- if( _SysIOCtl(1, DRV_IOCTL_TYPE, NULL) != DRV_TYPE_TERMINAL ) {
- fprintf(stderr, "note: assuming 80x25, can't get terminal dimensions\n");
- giTerminal_Width = 80;
- giTerminal_Height = 25;
- }
- else {
- struct ptydims dims;
- _SysIOCtl(1, PTY_IOCTL_GETDIMS, &dims);
- giTerminal_Width = dims.W;
- giTerminal_Height = dims.H;
- }
+ ACurses_Init();
printf("\x1B[?1047h");
- printf("\x1B[%i;%ir", 0, giTerminal_Height-1);
+ printf("\x1B[%i;%ir", 1, giTerminal_Height-1);
SetCursorPos(giTerminal_Height-1, 1);
printf("[(status)] ");
// HACK: Static server entry
// UCC (University [of Western Australia] Computer Club) IRC Server
- gWindow_Status.Server = Server_Connect( "UCC", "130.95.13.18", 6667 );
+ tServer *starting_server = Server_Connect( "UCC", "130.95.13.18", 6667 );
// Freenode (#osdev)
// gWindow_Status.Server = Server_Connect( "Freenode", "89.16.176.16", 6667 );
// Local servers
// gWindow_Status.Server = Server_Connect( "VMHost", "10.0.2.2", 6667 );
// gWindow_Status.Server = Server_Connect( "BitlBee", "192.168.1.39", 6667 );
- if( !gWindow_Status.Server )
+ if( !starting_server )
return -1;
- MainLoop();
+ Windows_SetStatusServer(starting_server);
- for( tServer *srv = gpServers; srv; srv = srv->Next )
- _SysClose(srv->FD);
+ MainLoop();
+
+ Servers_CloseAll("Client closing");
return 0;
}
printf("[(status)] ");
fflush(stdout);
- tReadline *readline_info = Readline_Init(1);
-
for( ;; )
{
fd_set readfds, errorfds;
- int maxFD = 0;
+ int nfds = 1;
FD_ZERO(&readfds);
FD_ZERO(&errorfds);
- FD_SET(0, &readfds); // stdin
- fflush(stdout);
+ Input_FillSelect(&nfds, &readfds);
+ Servers_FillSelect(&nfds, &readfds, &errorfds);
- // Fill server FDs in fd_set
- for( tServer *srv = gpServers; srv; srv = srv->Next )
- {
- FD_SET(srv->FD, &readfds);
- FD_SET(srv->FD, &errorfds);
- if( srv->FD > maxFD )
- maxFD = srv->FD;
- }
+ int rv = _SysSelect(nfds, &readfds, 0, &errorfds, NULL, 0);
+ if( rv < 0 ) break;
- int rv = _SysSelect(maxFD+1, &readfds, 0, &errorfds, NULL, 0);
- if( rv == -1 ) break;
-
- if(FD_ISSET(0, &readfds))
- {
- // User input
- char *cmd = Readline_NonBlock(readline_info);
- if( cmd )
- {
- if( cmd[0] )
- {
- ParseUserCommand(cmd);
- }
- free(cmd);
- // Prompt
- SetCursorPos(giTerminal_Height-1, 1);
- printf("\x1B[2K"); // Clear line
- if( gpCurrentWindow->Name[0] )
- printf("[%s:%s] ",
- gpCurrentWindow->Server->Name, gpCurrentWindow->Name);
- else
- printf("[(status)] ");
- }
- }
+ // user input
+ Input_HandleSelect(nfds, &readfds);
// Server response
- for( tServer *srv = gpServers; srv; srv = srv->Next )
- {
- if(FD_ISSET(srv->FD, &readfds))
- {
- if( ProcessIncoming(srv) != 0 ) {
- // Oops, error
- _SysDebug("ProcessIncoming failed on FD%i (Server %p %s)",
- srv->FD, srv, srv->Name);
- return 1;
- }
- }
-
- if(FD_ISSET(srv->FD, &errorfds))
- {
- _SysDebug("Error on FD%i (Server %p %s)",
- srv->FD, srv, srv->Name);
- return 1;
- }
- }
+ Servers_HandleSelect(nfds, &readfds, &errorfds);
}
return 0;
}
return 0;
}
-
-void Cmd_join(char *ArgString)
-{
- int pos=0;
- char *channel_name = GetValue(ArgString, &pos);
-
- if( gpCurrentWindow->Server )
- {
- gpCurrentWindow = Window_Create(gpCurrentWindow->Server, channel_name);
- Redraw_Screen();
- writef(gpCurrentWindow->Server->FD, "JOIN :%s\n", channel_name);
- }
-}
-
-void Cmd_quit(char *ArgString)
-{
- const char *quit_message = ArgString;
- if( quit_message == NULL || quit_message[0] == '\0' )
- quit_message = "/quit - Acess2 IRC Client";
-
- for( tServer *srv = gpServers; srv; srv = srv->Next )
- {
- writef(srv->FD, "QUIT :%s\n", quit_message);
- }
-
- exit(0);
-}
-
-void Cmd_window(char *ArgString)
-{
- int pos = 0;
- char *window_id = GetValue(ArgString, &pos);
- int window_num = atoi(window_id);
-
- if( window_num > 0 )
- {
- tWindow *win;
- window_num --; // Move to base 0
- // Get `window_num`th window
- for( win = gpWindows; win && window_num--; win = win->Next );
- if( win ) {
- gpCurrentWindow = win;
- Redraw_Screen();
- }
- // Otherwise, silently ignore
- }
- else
- {
- window_num = 1;
- for( tWindow *win = gpWindows; win; win = win->Next, window_num ++ )
- {
- if( win->Name[0] ) {
- Message_AppendF(NULL, MSG_TYPE_SERVER, "client", "",
- "%i: %s/%s", window_num, win->Server->Name, win->Name);
- }
- else {
- Message_AppendF(NULL, MSG_TYPE_SERVER, "client", "",
- "%i: (status)", window_num);
- }
- }
- }
-}
-
-const struct {
- const char *Name;
- void (*Fcn)(char *ArgString);
-} caCommands[] = {
- {"join", Cmd_join},
- {"quit", Cmd_quit},
- {"window", Cmd_window},
- {"win", Cmd_window},
- {"w", Cmd_window},
-};
-const int ciNumCommands = sizeof(caCommands)/sizeof(caCommands[0]);
-
-/**
- * \brief Handle a line from the prompt
- */
-int ParseUserCommand(char *String)
-{
- if( String[0] == '/' )
- {
- char *command;
- int pos = 0;
-
- command = GetValue(String, &pos)+1;
-
- // TODO: Prefix matches
- int cmdIdx = -1;
- for( int i = 0; i < ciNumCommands; i ++ )
- {
- if( strcmp(command, caCommands[i].Name) == 0 ) {
- cmdIdx = i;
- break;
- }
- }
- if( cmdIdx != -1 ) {
- caCommands[cmdIdx].Fcn(String+pos);
- }
- else
- {
- Message_AppendF(NULL, MSG_TYPE_SERVER, "client", "", "Unknown command %s", command);
- }
- }
- else
- {
- // Message
- // - Only send if server is valid and window name is non-empty
- if( gpCurrentWindow->Server && gpCurrentWindow->Name[0] )
- {
- Message_Append(gpCurrentWindow->Server, MSG_TYPE_STANDARD,
- gsNickname, gpCurrentWindow->Name, String);
- writef(gpCurrentWindow->Server->FD,
- "PRIVMSG %s :%s\n", gpCurrentWindow->Name,
- String
- );
- }
- }
-
- return 0;
-}
-
-/**
- * \brief Connect to a server
- */
-tServer *Server_Connect(const char *Name, const char *AddressString, short PortNumber)
-{
- tServer *ret;
-
- ret = calloc(1, sizeof(tServer) + strlen(Name) + 1);
-
- strcpy(ret->Name, Name);
-
- // Connect to the remove server
- ret->FD = OpenTCP( AddressString, PortNumber );
- if( ret->FD == -1 ) {
- fprintf(stderr, "%s: Unable to create socket\n", Name);
- return NULL;
- }
-
- // Append to open list
- ret->Next = gpServers;
- gpServers = ret;
-
- // Read some initial data
- Message_Append(NULL, MSG_TYPE_SERVER, Name, "", "Connection opened");
- ProcessIncoming(ret);
-
- // Identify
- writef(ret->FD, "USER %s %s %s : %s\n", gsUsername, gsHostname, AddressString, gsRealName);
- writef(ret->FD, "NICK %s\n", gsNickname);
- Message_Append(NULL, MSG_TYPE_SERVER, Name, "", "Identified");
- //printf("%s: Identified\n", Name);
-
- return ret;
-}
-
-tMessage *Message_AppendF(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message, ...)
-{
- va_list args;
- int len;
- va_start(args, Message);
- len = vsnprintf(NULL, 0, Message, args);
- va_end(args);
-
- char buf[len+1];
- va_start(args, Message);
- vsnprintf(buf, len+1, Message, args);
- va_end(args);
-
- return Message_Append(Server, Type, Source, Dest, buf);
-}
-
-tMessage *Message_Append(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message)
-{
- tWindow *win = NULL;
- int msgLen = strlen(Message);
-
- // Server==NULL indicates an internal message
- if( Server == NULL || Source[0] == '\0' )
- {
- win = &gWindow_Status;
- }
- // Determine if it's a channel or PM
- else if( Dest[0] == '#' || Dest[0] == '&' ) // TODO: Better determining here
- {
- for(win = gpWindows; win; win = win->Next)
- {
- if( win->Server == Server && strcmp(win->Name, Dest) == 0 )
- {
- break;
- }
- }
- if( !win ) {
- //win = Window_Create(Server, Dest);
- win = &gWindow_Status; // Stick it in the status window, just in case
- }
- }
- #if 0
- else if( strcmp(Dest, Server->Nick) != 0 )
- {
- // Umm... message for someone who isn't us?
- win = &gWindow_Status; // Stick it in the status window, just in case
- }
- #endif
- // Server message?
- else if( strchr(Source, '.') ) // TODO: And again, less hack please
- {
- #if 1
- for(win = gpWindows; win; win = win->Next)
- {
- if( win->Server == Server && strcmp(win->Name, Source) == 0 )
- {
- break;
- }
- }
- #endif
- if( !win ) {
- win = &gWindow_Status;
- }
-
- // Set source to the server name (instead of the hostname)
- Source = Server->Name;
- }
- // Private message
- else
- {
- for(win = gpWindows; win; win = win->Next)
- {
- if( win->Server == Server && strcmp(win->Name, Source) == 0 )
- {
- break;
- }
- }
- if( !win ) {
- win = Window_Create(Server, Dest);
- }
- }
-
- // Create message cache
- _SysDebug("Win (%s) msg: <%s> %s", win->Name, Source, Message);
- tMessage *ret;
- ret = malloc( sizeof(tMessage) + msgLen + 1 + strlen(Source) + 1 );
- ret->Source = ret->Data + msgLen + 1;
- strcpy(ret->Source, Source);
- strcpy(ret->Data, Message);
- ret->Type = Type;
- ret->Server = Server;
-
- // Append to window message list
- ret->Next = win->Messages;
- win->Messages = ret;
-
- // Print now if current window
- if( win == gpCurrentWindow )
- {
- printf("\33[s");
- printf("\33[T"); // Scroll down 1 (free space below)
- SetCursorPos(giTerminal_Height-2, 1);
- int prefixlen = strlen(Source) + 3;
- int avail = giTerminal_Width - prefixlen;
- int msglen = strlen(Message);
- printf("[%s] %.*s", Source, avail, Message);
- while( msglen > avail ) {
- msglen -= avail;
- Message += avail;
- printf("\33[T");
- SetCursorPos(giTerminal_Height-2, prefixlen+1);
- printf("%.*s", avail, Message);
- }
- printf("\x1b[u");
- }
-
- return ret;
-}
-
-tWindow *Window_Create(tServer *Server, const char *Name)
-{
- tWindow *ret, *prev = NULL;
- int num = 1;
-
- // Get the end of the list
- // TODO: Cache this instead
- for( ret = gpCurrentWindow; ret; prev = ret, ret = ret->Next )
- num ++;
-
- ret = malloc(sizeof(tWindow) + strlen(Name) + 1);
- ret->Messages = NULL;
- ret->Server = Server;
- ret->ActivityLevel = 1;
- strcpy(ret->Name, Name);
-
- if( prev ) {
- ret->Next = prev->Next;
- prev->Next = ret;
- }
- else { // Shouldn't happen really
- ret->Next = gpWindows;
- gpWindows = ret;
- }
-
-// printf("Win %i %s:%s created\n", num, Server->Name, Name);
-
- return ret;
-}
-
void Redraw_Screen(void)
{
- int y = 0;
- tMessage *msg;
-
printf("\x1B[2J"); // Clear screen
printf("\x1B[0;0H"); // Reset cursor
- msg = gpCurrentWindow->Messages;
-
- // TODO: Title bar?
-
- // Note: This renders from the bottom up
- for( y = giTerminal_Height - 1; y -- && msg; msg = msg->Next)
- {
- int msglen = strlen(msg->Data);
- int prefix_len = 3 + strlen(msg->Source);
- int line_avail = giTerminal_Width - prefix_len;
- int i = 0, done = 0;
-
- y -= msglen / line_avail; // Extra lines (y-- above handles the 1 line case)
- SetCursorPos(y, 1);
- printf("[%s] ", msg->Source);
-
- while(done < msglen) {
- done += printf("%.*s", line_avail, msg->Data+done);
- i ++;
- SetCursorPos(y+i, prefix_len+1);
- }
- }
-
- // Bottom line is rendered by the prompt
-}
-
-void Cmd_PRIVMSG(tServer *Server, const char *Dest, const char *Src, const char *Message)
-{
- Message_Append(Server, MSG_TYPE_STANDARD, Dest, Src, Message);
- //printf("<%s:%s:%s> %s\n", Server->Name, Dest, Src, Message);
-}
-
-void ParseServerLine_Numeric(tServer *Server, const char *ident, int Num, char *Line)
-{
- int pos = 0;
- const char *message;
- const char *user = GetValue(Line, &pos);
-
- if( Line[pos] == ':' ) {
- message = Line + pos + 1;
- }
- else {
- message = GetValue(Line, &pos);
- }
-
- switch(Num)
- {
- case 332: // Topic
- user = message; // Channel
- message = Line + pos + 1; // Topic
- Message_AppendF(Server, MSG_TYPE_SERVER, user, user, "Topic: %s", message);
- break;
- case 333: // Topic set by
- user = message; // Channel
- message = GetValue(Line, &pos); // User
- GetValue(Line, &pos); // Timestamp
- Message_AppendF(Server, MSG_TYPE_SERVER, user, user, "Set by %s", message);
- break;
- case 353: // /NAMES list
- // <user> = <channel> :list
- // '=' was eaten in and set to message
- user = GetValue(Line, &pos); // Actually channel
- message = Line + pos + 1; // List
- Message_AppendF(Server, MSG_TYPE_SERVER, user, user, "Names: %s", message);
- break;
- case 366: // end of /NAMES list
- // <user> <channel> :msg
- user = message;
- message = Line + pos + 1;
- Message_Append(Server, MSG_TYPE_SERVER, user, user, message);
- break;
- case 372: // MOTD Data
- case 375: // MOTD Start
- case 376: // MOTD End
-
- default:
- //printf("[%s] %i %s\n", Server->Name, num, message);
- Message_Append(Server, MSG_TYPE_SERVER, ident, user, message);
- break;
- }
-}
-
-void ParseServerLine_String(tServer *Server, const char *ident, const char *cmd, char *Line)
-{
- int pos = 0;
- _SysDebug("ident=%s,cmd=%s,Line=%s", ident, cmd, Line);
- if( strcmp(cmd, "NOTICE") == 0 )
- {
- const char *class = GetValue(Line, &pos);
- _SysDebug("NOTICE class='%s'", class);
-
- const char *message = (Line[pos] == ':') ? Line + pos + 1 : GetValue(Line, &pos);
-
- //printf("[%s] NOTICE %s: %s\n", Server->Name, ident, message);
- char *ident_bang = strchr(ident, '!');
- if( ident_bang ) {
- *ident_bang = '\0';
- }
- Message_Append(Server, MSG_TYPE_NOTICE, ident, "", message);
- }
- else if( strcmp(cmd, "PRIVMSG") == 0 )
- {
- const char *dest = GetValue(Line, &pos);
- const char *message = (Line[pos] == ':') ? Line + pos + 1 : GetValue(Line, &pos);
-
- // TODO: Catch when the privmsg is addressed to the user
-
-// Cmd_PRIVMSG(Server, dest, ident, message);
- char *ident_bang = strchr(ident, '!');
- if( ident_bang ) {
- *ident_bang = '\0';
- }
- Message_Append(Server, MSG_TYPE_STANDARD, ident, dest, message);
- }
- else if( strcmp(cmd, "JOIN" ) == 0 )
- {
- const char *channel = Line + pos + 1;
-
- Message_AppendF(Server, MSG_TYPE_JOIN, "", channel, "%s has joined", ident);
- //Window_Create(Server, channel);
- }
- else if( strcmp(cmd, "PART" ) == 0 )
- {
- const char *channel = Line + pos + 1;
-
- Message_AppendF(Server, MSG_TYPE_PART, "", channel, "%s has left", ident);
- //Window_Create(Server, channel);
- }
- else
- {
- Message_AppendF(Server, MSG_TYPE_SERVER, "", "", "Unknown message %s (%s)", cmd, Line);
- }
-}
-
-/**
- */
-void ParseServerLine(tServer *Server, char *Line)
-{
- int pos = 0;
-
- _SysDebug("[%s] %s", Server->Name, Line);
-
- // Message?
- if( *Line == ':' )
- {
- pos ++;
- const char *ident = GetValue(Line, &pos); // Ident (user or server)
- const char *cmd = GetValue(Line, &pos);
-
- // Numeric command
- if( isdigit(cmd[0]) && isdigit(cmd[1]) && isdigit(cmd[2]) )
- {
- int num;
- num = (cmd[0] - '0') * 100;
- num += (cmd[1] - '0') * 10;
- num += (cmd[2] - '0') * 1;
-
- ParseServerLine_Numeric(Server, ident, num, Line+pos);
- }
- else
- {
- ParseServerLine_String(Server, ident, cmd, Line+pos);
- }
- }
- else {
- const char *cmd = GetValue(Line, &pos);
-
- if( strcmp(cmd, "PING") == 0 ) {
- writef(Server->FD, "PONG %s\n", Line+pos);
- }
- else {
- // Command to client
- Message_AppendF(NULL, MSG_TYPE_UNK, "", "", "Client Command: %s", Line);
- }
- }
-}
-
-/**
- * \brief Process incoming lines from the server
- */
-int ProcessIncoming(tServer *Server)
-{
- char *ptr, *newline;
- int len;
-
- // While there is data in the buffer, read it into user memory and
- // process it line by line
- // ioctl#8 on a TCP client gets the number of bytes in the recieve buffer
- // - Used to avoid blocking
- #if NON_BLOCK_READ
- while( (len = _SysIOCtl(Server->FD, 8, NULL)) > 0 )
- {
- #endif
- // Read data
- len = _SysRead(Server->FD, &Server->InBuf[Server->ReadPos], BUFSIZ - Server->ReadPos);
- if( len == -1 ) {
- return -1;
- }
- Server->InBuf[Server->ReadPos + len] = '\0';
-
- // Break into lines
- ptr = Server->InBuf;
- while( (newline = strchr(ptr, '\n')) )
- {
- *newline = '\0';
- if( newline[-1] == '\r' ) newline[-1] = '\0';
- ParseServerLine(Server, ptr);
- ptr = newline + 1;
- }
-
- // Handle incomplete lines
- if( ptr - Server->InBuf < len + Server->ReadPos ) {
- // Update the read position
- // InBuf ReadPos ptr ReadPos+len
- // | old | new used | new unused |
- Server->ReadPos = len + Server->ReadPos - (ptr - Server->InBuf);
- // Copy stuff back (moving "new unused" to the start of the buffer)
- memcpy(Server->InBuf, ptr, Server->ReadPos);
- }
- else {
- Server->ReadPos = 0;
- }
- #if NON_BLOCK_READ
- }
- #endif
-
- return 0;
+ Windows_RepaintCurrent();
}
/**
return ret;
}
-void SetCursorPos(int Row, int Col)
-{
- printf("\x1B[%i;%iH", Row, Col);
-}
-
-static inline int isdigit(int ch)
-{
- return '0' <= ch && ch < '9';
-}
--- /dev/null
+/*
+ */
+#ifndef _MESSAGE_H_
+#define _MESSAGE_H_
+
+enum eMessageTypes
+{
+ MSG_TYPE_NULL,
+ MSG_TYPE_SERVER, // Server message
+
+ MSG_TYPE_NOTICE, // NOTICE command
+ MSG_TYPE_JOIN, // JOIN command
+ MSG_TYPE_PART, // PART command
+ MSG_TYPE_QUIT, // QUIT command
+
+ MSG_TYPE_STANDARD, // Standard line
+ MSG_TYPE_ACTION, // /me
+
+ MSG_TYPE_UNK
+};
+
+enum eMessageClass
+{
+ MSG_CLASS_BARE, // source is unused, just gets timestamped
+ MSG_CLASS_CLIENT, // source optional, prefixed by '-!-'
+ MSG_CLASS_WALL, // [SOURCE] MSG -- Server-provided message
+ MSG_CLASS_MESSAGE, // <SOURCE> MSG
+ MSG_CLASS_ACTION, // * SOURCE MSG
+};
+
+typedef struct sMessage tMessage;
+
+//extern tMessage *Message_AppendF(tServer *Server, int Type, const char *Src, const char *Dst, const char *Fmt, ...) __attribute__((format(__printf__,5,6)));
+//extern tMessage *Message_Append(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message);
+
+#endif
+
--- /dev/null
+/*
+ */
+#include "pseudo_curses.h"
+#include <acess/sys.h>
+#include <acess/devices/pty.h>
+#include <stdio.h>
+
+ int giTerminal_Width = 80;
+ int giTerminal_Height = 25;
+
+void ACurses_Init(void)
+{
+ if( _SysIOCtl(1, DRV_IOCTL_TYPE, NULL) != DRV_TYPE_TERMINAL ) {
+ _SysDebug("note: assuming 80x25, can't get terminal dimensions");
+ giTerminal_Width = 80;
+ giTerminal_Height = 25;
+ }
+ else {
+ struct ptydims dims;
+ _SysIOCtl(1, PTY_IOCTL_GETDIMS, &dims);
+ giTerminal_Width = dims.W;
+ giTerminal_Height = dims.H;
+ }
+}
+
+void SetCursorPos(int Row, int Col)
+{
+ printf("\x1B[%i;%iH", Row, Col);
+}
+
--- /dev/null
+/*
+ */
+#ifndef _ACURSES_H_
+#define _ACURSES_H_
+
+extern int giTerminal_Width;
+extern int giTerminal_Height;
+
+extern void ACurses_Init(void);
+extern void SetCursorPos(int Row, int Col);
+
+#endif
+
--- /dev/null
+/*
+ */
+#include <stdlib.h>
+#include <string.h>
+#include "server.h"
+#include "window.h"
+#include <acess/sys.h>
+#include <ctype.h> // isdigit
+#include <stdio.h> // vsnprintf
+
+// === PROTOTYPES ===
+tServer *Server_Connect(const char *Name, const char *AddressString, short PortNumber);
+ int Server_HandleIncoming(tServer *Server);
+void ParseServerLine(tServer *Server, char *Line);
+
+// === GLOBALS ===
+const char *gsUsername = "user";
+const char *gsHostname = "acess";
+const char *gsRealName = "Acess2 IRC Client";
+const char *gsNickname = "acess";
+tServer *gpServers;
+
+// === CODE ===
+void Servers_FillSelect(int *nfds, fd_set *rfds, fd_set *efds)
+{
+ for( tServer *srv = gpServers; srv; srv = srv->Next )
+ {
+ FD_SET(srv->FD, rfds);
+ FD_SET(srv->FD, efds);
+ if( srv->FD >= *nfds )
+ *nfds = srv->FD+1;
+ }
+}
+
+void Servers_HandleSelect(int nfds, const fd_set *rfds, const fd_set *efds)
+{
+ for( tServer *srv = gpServers; srv; srv = srv->Next )
+ {
+ if(FD_ISSET(srv->FD, rfds))
+ {
+ int rv = Server_HandleIncoming(srv);
+ if(rv)
+ {
+ // Oops, error
+ _SysDebug("ProcessIncoming failed on FD%i (Server %p %s)",
+ srv->FD, srv, srv->Name);
+ Exit("Processing error");
+ }
+ }
+
+ if(FD_ISSET(srv->FD, efds))
+ {
+ _SysDebug("Error on FD%i (Server %p %s)",
+ srv->FD, srv, srv->Name);
+ Exit("Socket error");
+ }
+ }
+}
+
+void Servers_CloseAll(const char *QuitMessage)
+{
+ while( gpServers )
+ {
+ tServer *srv = gpServers;
+ gpServers = srv->Next;
+
+ Server_SendCommand(srv, "QUIT :%s", QuitMessage);
+ _SysClose(srv->FD);
+ free(srv);
+ }
+}
+
+/**
+ * \brief Connect to a server
+ */
+tServer *Server_Connect(const char *Name, const char *AddressString, short PortNumber)
+{
+ tServer *ret;
+
+ ret = calloc(1, sizeof(tServer) + strlen(Name) + 1);
+
+ strcpy(ret->Name, Name);
+
+ // Connect to the remove server
+ ret->FD = OpenTCP( AddressString, PortNumber );
+ if( ret->FD == -1 ) {
+ free(ret);
+ Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Name, "Unable to create socket");
+ return NULL;
+ }
+
+ ret->Nick = strdup(gsNickname);
+
+ // Append to open list
+ ret->Next = gpServers;
+ gpServers = ret;
+
+ // Read some initial data
+ Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Name, "Unable to create socket");
+ Server_HandleIncoming(ret);
+
+ // Identify
+ Server_SendCommand(ret, "USER %s %s %s : %s", gsUsername, gsHostname, AddressString, gsRealName);
+ Server_SendCommand(ret, "NICK %s", ret->Nick);
+ Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Name, "Identified");
+
+ return ret;
+}
+
+const char *Server_GetNick(const tServer *Server)
+{
+ return Server->Nick;
+}
+
+void Server_SendCommand(tServer *Server, const char *Format, ...)
+{
+ va_list args;
+ int len;
+
+ va_start(args, Format);
+ len = vsnprintf(NULL, 0, Format, args);
+ va_end(args);
+
+ char buf[len+1];
+ va_start(args, Format);
+ vsnprintf(buf, len+1, Format, args);
+ va_end(args);
+
+ _SysWrite(Server->FD, buf, len);
+ _SysWrite(Server->FD, "\n", 1);
+}
+
+void Cmd_PRIVMSG(tServer *Server, const char *Dest, const char *Src, const char *Message)
+{
+ tWindow *win;
+ if( strcmp(Dest, Server->Nick) == 0 ) {
+ win = Windows_GetByName(Server, Src);
+ if(!win)
+ win = Window_Create(Server, Src);
+ }
+ else {
+ win = Windows_GetByName(Server, Dest);
+ if(!win)
+ win = Window_Create(Server, Dest);
+ }
+ Window_AppendMessage(win, MSG_CLASS_MESSAGE, Src, "%s", Message);
+}
+
+/**
+ * \brief Process incoming lines from the server
+ */
+int Server_HandleIncoming(tServer *Server)
+{
+ char *ptr, *newline;
+ int len;
+
+ // While there is data in the buffer, read it into user memory and
+ // process it line by line
+ // ioctl#8 on a TCP client gets the number of bytes in the recieve buffer
+ // - Used to avoid blocking
+ #if NON_BLOCK_READ
+ while( (len = _SysIOCtl(Server->FD, 8, NULL)) > 0 )
+ {
+ #endif
+ // Read data
+ len = _SysRead(Server->FD, &Server->InBuf[Server->ReadPos], sizeof(Server->InBuf) - Server->ReadPos);
+ if( len == -1 ) {
+ return -1;
+ }
+ Server->InBuf[Server->ReadPos + len] = '\0';
+
+ // Break into lines
+ ptr = Server->InBuf;
+ while( (newline = strchr(ptr, '\n')) )
+ {
+ *newline = '\0';
+ if( newline[-1] == '\r' ) newline[-1] = '\0';
+ ParseServerLine(Server, ptr);
+ ptr = newline + 1;
+ }
+
+ // Handle incomplete lines
+ if( ptr - Server->InBuf < len + Server->ReadPos ) {
+ // Update the read position
+ // InBuf ReadPos ptr ReadPos+len
+ // | old | new used | new unused |
+ Server->ReadPos = len + Server->ReadPos - (ptr - Server->InBuf);
+ // Copy stuff back (moving "new unused" to the start of the buffer)
+ memcpy(Server->InBuf, ptr, Server->ReadPos);
+ }
+ else {
+ Server->ReadPos = 0;
+ }
+ #if NON_BLOCK_READ
+ }
+ #endif
+
+ return 0;
+}
+
+void ParseServerLine_Numeric(tServer *Server, const char *ident, int Num, char *Line)
+{
+ int pos = 0;
+ const char *message;
+ const char *user = GetValue(Line, &pos);
+ const char *timestamp;
+
+ if( Line[pos] == ':' ) {
+ message = Line + pos + 1;
+ }
+ else {
+ message = GetValue(Line, &pos);
+ }
+
+ switch(Num)
+ {
+ case 332: // Topic
+ user = message; // Channel
+ message = Line + pos + 1; // Topic
+ Window_AppendMsg_Topic( Windows_GetByNameOrCreate(Server, user), message );
+ break;
+ case 333: // Topic set by
+ user = message; // Channel
+ message = GetValue(Line, &pos); // User
+ timestamp = GetValue(Line, &pos); // Timestamp
+ Window_AppendMsg_TopicTime( Windows_GetByNameOrCreate(Server, user), message, timestamp );
+ break;
+ case 353: // /NAMES list
+ // <user> = <channel> :list
+ // '=' was eaten in and set to message
+ user = GetValue(Line, &pos); // Actually channel
+ message = Line + pos + 1; // List
+ // TODO: parse and store
+ Window_AppendMessage( Windows_GetByNameOrCreate(Server, user), MSG_CLASS_CLIENT, "NAMES", message );
+ break;
+ case 366: // end of /NAMES list
+ // <user> <channel> :msg
+ // - Ignored
+ break;
+ case 372: // MOTD Data
+ case 375: // MOTD Start
+ case 376: // MOTD End
+
+ default:
+ //printf("[%s] %i %s\n", Server->Name, num, message);
+ Window_AppendMessage( WINDOW_STATUS, MSG_CLASS_WALL, Server->Name, "Unknown %i %s", Num, message);
+ break;
+ }
+}
+
+void ParseServerLine_String(tServer *Server, const char *ident, const char *cmd, char *Line)
+{
+ int pos = 0;
+ _SysDebug("ident=%s,cmd=%s,Line=%s", ident, cmd, Line);
+ if( strcmp(cmd, "NOTICE") == 0 )
+ {
+ const char *class = GetValue(Line, &pos);
+ _SysDebug("NOTICE class='%s'", class);
+
+ const char *message = (Line[pos] == ':') ? Line + pos + 1 : GetValue(Line, &pos);
+
+ //printf("[%s] NOTICE %s: %s\n", Server->Name, ident, message);
+ char *ident_bang = strchr(ident, '!');
+ if( ident_bang ) {
+ *ident_bang = '\0';
+ }
+ // TODO: Colour codes
+ Window_AppendMessage( WINDOW_STATUS, MSG_CLASS_WALL, Server->Name, "%s %s", ident, message);
+ }
+ else if( strcmp(cmd, "PRIVMSG") == 0 )
+ {
+ const char *dest = GetValue(Line, &pos);
+ const char *message = (Line[pos] == ':') ? Line + pos + 1 : GetValue(Line, &pos);
+
+ char *ident_bang = strchr(ident, '!');
+ if( ident_bang ) {
+ *ident_bang = '\0';
+ }
+ Cmd_PRIVMSG(Server, dest, ident, message);
+ }
+ else if( strcmp(cmd, "JOIN" ) == 0 )
+ {
+ const char *channel = Line + pos + 1;
+
+ Window_AppendMsg_Join( Windows_GetByNameOrCreate(Server, channel), ident );
+ }
+ else if( strcmp(cmd, "PART" ) == 0 )
+ {
+ const char *channel = Line + pos + 1;
+
+ Window_AppendMsg_Part( Windows_GetByNameOrCreate(Server, channel), ident, "" );
+ }
+ else
+ {
+ Window_AppendMessage( WINDOW_STATUS, MSG_CLASS_BARE, Server->Name, "Unknown command '%s' %s", cmd, Line);
+ }
+}
+
+/**
+ */
+void ParseServerLine(tServer *Server, char *Line)
+{
+ int pos = 0;
+
+ _SysDebug("[%s] %s", Server->Name, Line);
+
+ // Message?
+ if( *Line == ':' )
+ {
+ pos ++;
+ const char *ident = GetValue(Line, &pos); // Ident (user or server)
+ const char *cmd = GetValue(Line, &pos);
+
+ // Numeric command
+ if( isdigit(cmd[0]) && isdigit(cmd[1]) && isdigit(cmd[2]) )
+ {
+ int num;
+ num = (cmd[0] - '0') * 100;
+ num += (cmd[1] - '0') * 10;
+ num += (cmd[2] - '0') * 1;
+
+ ParseServerLine_Numeric(Server, ident, num, Line+pos);
+ }
+ else
+ {
+ ParseServerLine_String(Server, ident, cmd, Line+pos);
+ }
+ }
+ else {
+ const char *cmd = GetValue(Line, &pos);
+
+ if( strcmp(cmd, "PING") == 0 ) {
+ Server_SendCommand(Server, "PONG %s", Line+pos);
+ }
+ else {
+ // Command to client
+ Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Server->Name, "UNK Client Command: %s", Line);
+ }
+ }
+}
--- /dev/null
+/*
+ */
+#ifndef _SERVER_H_
+#define _SERVER_H_
+
+#include "common.h"
+
+typedef struct sServer {
+ struct sServer *Next;
+ int FD;
+ char InBuf[1024+1];
+ int ReadPos;
+ char *Nick;
+ char Name[];
+} tServer;
+
+extern void Servers_FillSelect(int *nfds, fd_set *rfds, fd_set *efds);
+extern void Servers_HandleSelect(int nfds, const fd_set *rfds, const fd_set *efds);
+extern void Servers_CloseAll(const char *QuitMessage);
+
+extern tServer *Server_Connect(const char *Name, const char *AddressString, short PortNumber);
+extern int Server_HandleIncoming(tServer *Server);
+
+extern const char *Server_GetNick(const tServer *Server);
+
+extern void Server_SendCommand(tServer *Server, const char *Format, ...) __attribute__((format(__printf__,2,3)));
+
+#endif
+
--- /dev/null
+/*
+ */
+#include "window.h"
+#include <stddef.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h> // TODO: replace with calls into ACurses_*
+#include <stdlib.h>
+#include <assert.h>
+
+struct sMessage
+{
+ struct sMessage *Next;
+ time_t Timestamp;
+ enum eMessageClass Class;
+ char *Source; // Pointer to the end of `Data`
+ char Data[];
+};
+
+struct sWindow
+{
+ struct sWindow *Next;
+ tMessage *Messages;
+ tServer *Server; //!< Canonical server (can be NULL)
+ int ActivityLevel;
+ char Name[]; // Channel name / remote user
+};
+
+// === PROTOTYPES ===
+void Windows_RepaintCurrent(void);
+ int Windows_int_PaintMessage(tMessage *Message);
+
+// === GLOBALS ===
+tWindow gWindow_Status = {
+ NULL, NULL, NULL, // No next, empty list, no server
+ 0, {""} // No activity, empty name (rendered as status)
+};
+tWindow *gpWindows = &gWindow_Status;
+tWindow *gpCurrentWindow = &gWindow_Status;
+
+// === CODE ===
+void Windows_RepaintCurrent(void)
+{
+ tMessage *msg = gpCurrentWindow->Messages;
+
+ // TODO: Title bar?
+
+ // Note: This renders from the bottom up
+ for( int y = giTerminal_Height - 1; y -- && msg; msg = msg->Next)
+ {
+ y -= Windows_int_PaintMessage(msg);
+ }
+
+ // Bottom line is rendered by the prompt
+
+}
+
+int Windows_int_PaintMessage(tMessage *Message)
+{
+ printf("\33[T"); // Scroll down 1 (free space below)
+ SetCursorPos(giTerminal_Height-2, 1);
+
+ size_t prefixlen = 0;
+ prefixlen += printf("%02i:%02i:%02i ", (Message->Timestamp/3600)%24, (Message->Timestamp/60)%60, Message->Timestamp%60);
+ switch(Message->Class)
+ {
+ case MSG_CLASS_BARE: break;
+ case MSG_CLASS_CLIENT:
+ if(Message->Source)
+ prefixlen += printf("[%s] ", Message->Source);
+ prefixlen += printf("-!- ");
+ break;
+ case MSG_CLASS_WALL:
+ prefixlen += printf("[%s] ", Message->Source);
+ break;
+ case MSG_CLASS_MESSAGE:
+ prefixlen += printf("<%s> ", Message->Source);
+ break;
+ case MSG_CLASS_ACTION:
+ prefixlen += printf("* %s ", Message->Source);
+ break;
+ }
+ int avail = giTerminal_Width - prefixlen;
+ int msglen = strlen(Message->Data);
+
+ int nLines = 1;
+ printf("%.*s", avail, Message);
+ while( msglen > avail ) {
+ msglen -= avail;
+ Message += avail;
+ printf("\33[T");
+ SetCursorPos(giTerminal_Height-2, prefixlen+1);
+ printf("%.*s", avail, Message);
+ nLines ++;
+ }
+
+ return nLines;
+}
+
+void Windows_SwitchTo(tWindow *Window)
+{
+ gpCurrentWindow = Window;
+ Redraw_Screen();
+}
+
+void Windows_SetStatusServer(tServer *Server)
+{
+ gWindow_Status.Server = Server;
+}
+
+tWindow *Windows_GetByIndex(int Index)
+{
+ tWindow *win;
+ for( win = gpWindows; win && Index--; win = win->Next )
+ ;
+ return win;
+}
+
+tWindow *Windows_GetByNameEx(tServer *Server, const char *Name, bool CreateAllowed)
+{
+ tWindow *ret, *prev = NULL;
+ int num = 1;
+
+ // Get the end of the list and check for duplicates
+ // TODO: Cache this instead
+ for( ret = &gWindow_Status; ret; prev = ret, ret = ret->Next )
+ {
+ if( ret->Server == Server && strcmp(ret->Name, Name) == 0 )
+ {
+ return ret;
+ }
+ num ++;
+ }
+ if( !CreateAllowed ) {
+ return NULL;
+ }
+
+ ret = malloc(sizeof(tWindow) + strlen(Name) + 1);
+ ret->Messages = NULL;
+ ret->Server = Server;
+ ret->ActivityLevel = 1;
+ strcpy(ret->Name, Name);
+
+ if( prev ) {
+ ret->Next = prev->Next;
+ prev->Next = ret;
+ }
+ else { // Shouldn't happen really
+ ret->Next = gpWindows;
+ gpWindows = ret;
+ }
+
+// printf("Win %i %s:%s created\n", num, Server->Name, Name);
+
+ return ret;
+}
+
+tWindow *Windows_GetByName(tServer *Server, const char *Name)
+{
+ return Windows_GetByNameEx(Server, Name, false);
+}
+
+tWindow *Window_Create(tServer *Server, const char *Name)
+{
+ return Windows_GetByNameEx(Server, Name, true);
+}
+
+
+tWindow *Window_int_ParseSpecial(const tWindow *Window)
+{
+ if( Window == NULL )
+ return gpCurrentWindow;
+ if( Window == WINDOW_STATUS )
+ return &gWindow_Status;
+ return (tWindow*)Window;
+}
+
+const char *Window_GetName(const tWindow *Window) {
+ return Window_int_ParseSpecial(Window)->Name;
+}
+tServer *Window_GetServer(const tWindow *Window) {
+ return Window_int_ParseSpecial(Window)->Server;
+}
+bool Window_IsChat(const tWindow *Window) {
+ return Window_int_ParseSpecial(Window) != &gWindow_Status;
+}
+
+// -----------------------------
+// Messages
+// -----------------------------
+void Window_AppendMessage(tWindow *Window, enum eMessageClass Class, const char *Source, const char *Message, ...)
+{
+ Window = Window_int_ParseSpecial(Window);
+
+ va_list args;
+
+ va_start(args, Message);
+ size_t len = vsnprintf(NULL, 0, Message, args);
+ va_end(args);
+
+ tMessage *msg = malloc( sizeof(tMessage) + len+1 + (Source?strlen(Source)+1:0) );
+ assert(msg);
+
+ msg->Class = Class;
+ msg->Source = (Source ? msg->Data + len+1 : NULL);
+ va_start(args, Message);
+ vsnprintf(msg->Data, len+1, Message, args);
+ va_end(args);
+
+ msg->Next = Window->Messages;
+ Window->Messages = msg;
+
+ if( Window == gpCurrentWindow )
+ {
+ // Scroll if needed, and redraw?
+ // - Lazy option of draw at bottom of screen
+ printf("\33[s"); // Save cursor
+ Windows_int_PaintMessage(msg);
+ printf("\x1b[u"); // Restore cursor
+ }
+}
+
+void Window_AppendMsg_Join(tWindow *Window, const char *Usermask)
+{
+ Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "%s has joined %s", Usermask, Window->Name);
+}
+void Window_AppendMsg_Quit(tWindow *Window, const char *Usermask, const char *Reason)
+{
+ Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "%s has joined quit (%s)", Usermask, Reason);
+}
+void Window_AppendMsg_Part(tWindow *Window, const char *Usermask, const char *Reason)
+{
+ Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "%s has joined left %s (%s)", Usermask, Window->Name, Reason);
+}
+void Window_AppendMsg_Topic(tWindow *Window, const char *Topic)
+{
+ Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "Topic of %s is %s", Window->Name, Topic);
+}
+void Window_AppendMsg_TopicTime(tWindow *Window, const char *User, const char *Timestamp)
+{
+ Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "Topic set by %s at %s", User, Timestamp);
+}
+
--- /dev/null
+/*
+ */
+#ifndef _WINDOW_H_
+#define _WINDOW_H_
+
+#include "common.h"
+#include "message.h"
+#include <stdbool.h>
+
+typedef struct sWindow tWindow;
+extern void Windows_RepaintCurrent(void);
+
+extern void Windows_SetStatusServer(tServer *Server);
+extern tWindow *Window_Create(tServer *Server, const char *Name);
+extern tWindow *Windows_GetByIndex(int Index);
+extern tWindow *Windows_GetByName(tServer *Server, const char *Name);
+static inline tWindow *Windows_GetByNameOrCreate(tServer *Server, const char *Name) {
+ return Window_Create(Server, Name);
+}
+extern void Windows_SwitchTo(tWindow *Window);
+
+extern void Window_AppendMessage(tWindow *Window, enum eMessageClass Class, const char *Source, const char *Message, ...)
+ __attribute__((format(__printf__,4,5)));
+extern void Window_AppendMsg_Join(tWindow *Window, const char *Usermask);
+extern void Window_AppendMsg_Quit(tWindow *Window, const char *Usermask, const char *Reason);
+extern void Window_AppendMsg_Part(tWindow *Window, const char *Usermask, const char *Reason);
+extern void Window_AppendMsg_Topic(tWindow *Window, const char *Topic);
+extern void Window_AppendMsg_TopicTime(tWindow *Window, const char *User, const char *Timestmap);
+
+extern const char *Window_GetName(const tWindow *Window);
+extern tServer *Window_GetServer(const tWindow *Window);
+extern bool Window_IsChat(const tWindow *Window);
+
+#define WINDOW_STATUS ((void*)-1)
+
+#endif
.PHONY: _libs
+.PRECIOUS: .no
-HEADERS := $(patsubst include_exp/%,../../include/%,$(shell find include_exp/ -name \*.h))
+HEADERS := $(patsubst include_exp/%,../../include/%,$(shell find include_exp/ -name \*.h 2>/dev/null))
_libs: $(HEADERS)
../../include/%: include_exp/%
@echo > /dev/null
utest-build: $(UTESTS:%=TEST_%)
+ @echo > /dev/null
utest-run: $(UTESTS:%=runtest-%)
@echo > /dev/null
obj-native/%.no: %.c
@mkdir -p $(dir $@)
+ $(NCC) -
g -c $< -o $@ -Wall -std=gnu99 -MD -MP -MF [email protected] '-D_SysDebug(f,v...)=fprintf(stderr,"DEBUG "f"\n",##v)' -include stdio.h
TEST_%: obj-native/TEST_%.no obj-native/%.no
- $(NCC) -o $@ $^
+ $(NCC) -g -o $@ $^
+
+.SECONDARY: %.no
-include $(UTESTS:%=obj-native/TEST_%.no.dep)
-include $(UTESTS:%=obj-native/%.no.dep)
BIN = $(OUTPUTDIR)Libs/acess.ld
-.PHONY: all clean install utest generate_exp
+.PHONY: all clean install utest utest-build utest-run generate_exp
all: $(BIN)
install: $(BIN)
# How does one unit test a linker script?
-utest generate_exp:
+utest generate_exp utest-build utest-run:
@echo > /dev/null
$(BIN): acess_$(ARCHDIR).ld.h
BIN = $(OUTPUTDIR)Libs/crt0.o $(OUTPUTDIR)Libs/crtbegin.o $(OUTPUTDIR)Libs/crtend.o
-.PHONY: all clean install utest generate_exp
+.PHONY: all clean install utest utest-build generate_exp
all: $(BIN)
$(RM) $(BIN)
# Disabled unit tests
-utest generate_exp:
+utest generate_exp utest-build utest-run:
@echo > /dev/null
$(OUTPUTDIR)Libs/%.o: %.c
[global %1:func]
%1:
push rbx
+ push rbp
mov eax, %2
SYSCALL_OP
mov [DWORD rel _errno], ebx
+ pop rbp
pop rbx
ret
%endmacro
*/
#include <stdio.h>
#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <string.h> // strerror
-#define TST(t, class, base, val, exp, fmt) do {\
- t ret = strto##class(#val, NULL, base); \
+#define STR_(v) #v
+#define STR(v) STR_(v)
+#define TST(t, class, base, val, exp, fmt, ofs, exp_errno) do {\
+ const char *in = val;\
+ char *end;\
+ errno = 0;\
+ t ret = strto##class(in, &end, base); \
if( ret != exp ) \
- printf("FAIL strto"#class"('"#val"') != "#val" (act 0x"fmt")\n", ret);\
+ fprintf(stderr, "FAIL strto"#class"('%s') != "#exp" (act "fmt")\n", in, ret);\
+ if( end != in+ofs ) \
+ fprintf(stderr, "FAIL strto"#class"('%s') returned wrong end: %p (+%zi) instead of %p (+%zi)\n",\
+ in,end,end-in,in+ofs,(size_t)ofs);\
+ if( exp_errno != errno ) \
+ fprintf(stderr, "FAIL strto"#class"('%s') returned wrong errno, exp '%s', got '%s'\n",\
+ in, strerror(exp_errno), strerror(errno));\
}while(0)
+#define PRIMEBUF(fmt, val) buf_len = snprintf(buf, sizeof(buf), fmt, val)
+
int main(int argc, char *argv[])
{
- TST(unsigned long, ul, 0, 0x10ec, 0x10ec, "%lx");
- TST(unsigned long long, ull, 0, 0xffeed10ec, 0xffeed10ec, "%llx");
+ char buf[64];
+ size_t buf_len;
+
+ // Success cases
+ TST(unsigned long, ul, 0, "0x10ec", 0x10ec, "%lx", 2+4, 0);
+ TST(unsigned long long, ull, 0, "0xffeed10ec", 0xffeed10ec, "%llx", 2+9, 0);
+ TST(unsigned long long, ull, 0, "01234567", 01234567, "%llo", 8, 0);
+ TST(unsigned long long, ull, 0, "1234567", 1234567, "%lld", 7, 0);
+ TST(long long, ll, 0, "-1", -1, "%lld", 2, 0); // -1
+ TST(long long, ll, 0, "100113", 100113, "%lld", strlen(in), 0);
+ TST(long long, ll, 0, "0x101", 0x101, "0x%llx", strlen(in), 0);
+
+ // Invalid strings
+ TST(unsigned long long, ull, 0, "0x", 0, "%llx", 1, 0); // Single 0
+ TST(unsigned long long, ull, 0, "0xg", 0, "%llx", 1, 0); // Single 0
+ TST(unsigned long long, ull, 0, "-a", 0, "%llx", 0, 0); // Nothing
+ TST(long long, ll, 0, "-a", 0, "%lld", 0, 0); // Nothing
+ TST(long long, ll, 0, "-1aaatg", -1, "%lld", 2, 0); // -1 (with traling junk)
+ TST(long long, ll, 0, "-+1aaatg", 0, "%lld", 0, 0); // Nothing
+ TST(long long, ll, 0, "- 1", 0, "%lld", 0, 0); // Nothing
+ TST(long long, ll, 0, "01278 1", 0127, "%lld", 4, 0); // 0127 with junk
+
+ // Range edges
+ PRIMEBUF("0x%llx", ULLONG_MAX);
+ TST(unsigned long long, ull, 0, buf, ULLONG_MAX, "0x%llx", buf_len, 0);
+ PRIMEBUF("%llu", ULLONG_MAX);
+ TST(unsigned long long, ull, 0, buf, ULLONG_MAX, "%llu", buf_len, 0);
+ PRIMEBUF("-%llu", (long long)LONG_MAX);
+ TST(long, l, 0, buf, -LONG_MAX, "%ld", buf_len, 0);
+
+ // Out of range
+ // - When the range limit is hit, valid characters should still be consumed (just not used)
+ TST(unsigned long long, ull, 0, "0x10000FFFF0000FFFF", ULLONG_MAX, "%llx", strlen(in), ERANGE);
+ TST(unsigned long, ul, 0, "0x10000FFFF0000FFFF", ULONG_MAX, "%lx", strlen(in), ERANGE);
+ TST(long, l, 0, "0x10000FFFF0000FFFF", LONG_MAX, "%ld", strlen(in), ERANGE);
+ TST(long, l, 0, "-0x10000FFFF0000FFFF", LONG_MIN, "%ld", strlen(in), ERANGE);
+ if( LONG_MIN < -LONG_MAX )
+ {
+ // Ensure that if -LONG_MIN is greater than LONG_MAX, that converting it leaves a range error
+ PRIMEBUF("%ld", LONG_MIN);
+ TST(long, l, 0, buf+1, LONG_MAX, "%ld", buf_len-1, ERANGE);
+ }
}
return isalpha(ch) || isdigit(ch);
}
-int toupper(int ch) {
- if('a'<=ch && ch <='z')
- return ch - 'a' + 'A';
- return ch;
+int isxdigit(int ch) {
+ if('0'<=ch&&ch<='9') return 1;
+ if('a'<=ch&&ch<='f') return 1;
+ if('F'<=ch&&ch<='F') return 1;
+ return 0;
}
-int tolower(int ch) {
- if('A'<=ch && ch <='Z')
- return ch - 'A' + 'a';
- return ch;
+
+int isupper(int ch) {
+ if('A'<=ch && ch <='Z') return 1;
+ return 0;
+}
+
+int islower(int ch) {
+ if('a'<=ch && ch <='z') return 1;
+ return 0;
+}
+
+int ispunct(int ch) {
+ if( isprint(ch) && !isspace(ch) && !isalnum(ch) )
+ return 1;
+ return 0;
}
int isprint(int ch ) {
- if( ch < ' ' ) return 0;
- if( ch > 'z' ) return 0;
+ if( ' ' <= ch && ch <= 'z' ) return 1;
return 1;
}
+int isgraph(int ch) {
+ // Anything but space
+ if( ' ' < ch && ch <= 'z' ) return 1;
+ return 0;
+}
+
int isspace(int ch) {
if(ch == ' ') return 1;
if(ch == '\t') return 1;
return 0;
}
-int isxdigit(int ch) {
- if('0'<=ch&&ch<='9') return 1;
- if('a'<=ch&&ch<='f') return 1;
- if('F'<=ch&&ch<='F') return 1;
- return 0;
+int toupper(int ch) {
+ if('a'<=ch && ch <='z')
+ return ch - 'a' + 'A';
+ return ch;
}
+int tolower(int ch) {
+ if('A'<=ch && ch <='Z')
+ return ch - 'A' + 'a';
+ return ch;
+}
+
// C99
int isblank(int ch) {
switch((enum libc_eErrorNumbers)errnum)
{
case EOK: return "Success";
+ case ERANGE: return "Value out of range";
+ case EDOM: return "Value out of domain";
+ case EILSEQ: return "Illegal character sequence";
+
case ENOSYS: return "Invalid instruction/syscall";
case EINVAL: return "Bad argument(s)";
case EBADF: return "Invalid file";
case ENOMEM: return "No free memory";
case EACCES: return "Not permitted";
case EBUSY: return "Resource is busy";
- case ERANGE: return "Value out of range";
case ENOTFOUND: return "Item not found";
case EROFS: return "Read only filesystem";
case ENOTIMPL: return "Not implimented";
case ENOTTY: return "Not a TTY";
case EAGAIN: return "Try again";
case EFBIG: return "File too big";
+ case E2BIG: return "Value too big";
case EALREADY: return "Operation was no-op";
+ case ENOSPC: return "No space left on the device";
+
case EAFNOSUPPORT: return "Address family not supported";
+ case EADDRINUSE: return "Address already in use";
+ case ETIMEDOUT: return "Operation timed out";
+
case EINTERNAL: return "Internal error";
}
_SysDebug("strerror: errnum=%i unk", errnum);
#include <acess/sys.h>\r
#include <stdlib.h>\r
#include <string.h>\r
+#include <assert.h>\r
#include "lib.h"\r
\r
#if 0\r
\r
// Check for free space after the block\r
heap_head *nexthead = NEXT_HEAD(head);\r
- if( nexthead && nexthead->magic == MAGIC_FREE && head->size + nexthead->size >= reqd_size )\r
+ assert( nexthead <= _heap_end );\r
+ if( nexthead != _heap_end && nexthead->magic == MAGIC_FREE && head->size + nexthead->size >= reqd_size )\r
{\r
// Split next block\r
if( head->size + nexthead->size > reqd_size )\r
void *ret = _malloc(bytes, __builtin_return_address(0));\r
if(ret == NULL)\r
return NULL;\r
+ heap_head *newhead = (heap_head*)ret - 1;\r
\r
- //Copy Old Data\r
+ // Copy Old Data\r
+ assert( head->size < newhead->size );\r
size_t copy_size = head->size-sizeof(heap_head)-sizeof(heap_foot);\r
- if( copy_size > bytes )\r
- copy_size = bytes;\r
- memcpy(ret, oldPos, bytes);\r
+ memcpy(ret, oldPos, copy_size);\r
free(oldPos);\r
\r
//Return\r
extern int isalpha(int ch);
extern int isdigit(int ch);
-
extern int isalnum(int ch);
+extern int isxdigit(int ch);
-extern int toupper(int ch);
-extern int tolower(int ch);
+extern int islower(int ch);
+extern int isupper(int ch);
+extern int ispunct(int ch);
extern int isprint(int ch);
+extern int isgraph(int ch);
extern int isspace(int ch);
-extern int isxdigit(int ch);
+extern int iscntrl(int ch);
// C99
extern int isblank(int ch);
+// Conversions
+extern int toupper(int ch);
+extern int tolower(int ch);
+
#ifdef __cplusplus
}
#endif
enum libc_eErrorNumbers {
EOK,
+ EDOM, // (C99) Value out of domain
+ EILSEQ, // (C99) Illegal multi-byte sequence
+ ERANGE, // (C99) Value out of range
+
ENOSYS, // Invalid Instruction
EINVAL, // Invalid Paramater
EBADF, // Bad FD
ENOMEM, // No free memory
EACCES, // Not permitted
EBUSY, // Resource is busy
- ERANGE, // Value out of range
ENOTFOUND, // Item not found
EROFS, // Read only
ENOTIMPL, // Not implemented
EAGAIN, // Try again
EALREADY, // Operation was a NOP
+ ENOSPC, // (POSIX) No space left on device
EFBIG, // File too large
+ E2BIG, // Argument list too large
// psockets
EAFNOSUPPORT,
+ EADDRINUSE, // Specified addres is already in use
+ ETIMEDOUT,
EINTERNAL // Internal Error
};
extern FILE *fdopen(int fd, const char *modes);
extern FILE *tmpfile(void);
extern int fclose(FILE *fp);
-extern void fflush(FILE *fp);
+extern int fflush(FILE *fp);
extern off_t ftell(FILE *fp);
extern off_t ftello(FILE *fp);
extern int fseek(FILE *fp, long int amt, int whence);
extern char *strstr(const char *str1, const char *str2);
extern size_t strcspn(const char *haystack, const char *reject);
extern size_t strspn(const char *haystack, const char *accept);
+extern char *strpbrk(const char *haystack, const char *accept);
extern char *strtok(char *str, const char *delim);
extern char *strtok_r(char *str, const char *delim, char **saveptr);
return ret;\r
}\r
\r
-EXPORT void fflush(FILE *fp)\r
+EXPORT int fflush(FILE *fp)\r
{\r
if( !fp || fp->FD == FD_NOTOPEN )\r
- return ;\r
+ return EBADF;\r
\r
// Nothing to do for memory files\r
if( fp->FD == FD_MEMFILE )\r
- return ;\r
+ return 0;\r
// Memory streams, update pointers\r
if( fp->FD == FD_MEMSTREAM ) {\r
*fp->BufPtr = fp->Buffer;\r
*fp->LenPtr = fp->BufferPos;\r
- return ;\r
+ return 0;\r
}\r
\r
_fflush_int(fp);\r
+ return 0;\r
}\r
\r
EXPORT void clearerr(FILE *fp)\r
return ret;
}
+EXPORT char *strpbrk(const char *haystack, const char *accept)
+{
+ while( *haystack )
+ {
+ for( int i = 0; accept[i]; i ++ )
+ {
+ if( accept[i] == *haystack )
+ return (char*)haystack;
+ }
+ }
+ return NULL;
+}
+
char *strtok(char *str, const char *delim)
{
static char *__saveptr;
unsigned long long strtoull(const char *str, char **end, int base)
{
- long long ret = 0;
+ unsigned long long ret = 0;
if( !str || base < 0 || base > 36 || base == 1 ) {
if(end)
// Handle base detection for hex
if( base == 0 || base == 16 ) {
- if( *str == '0' && str[1] == 'x' ) {
+ if( *str == '0' && (str[1] == 'x' || str[1] == 'X') && isxdigit(str[2]) ) {
str += 2;
base = 16;
}
if( base == 0 )
base = 10;
+ // Value before getting within 1 digit of ULLONG_MAX
+ // - Used to avoid overflow in more accurate check
+ unsigned long long max_before_ullong_max = ULLONG_MAX / base;
+ unsigned int space_above = ULLONG_MAX - max_before_ullong_max * base;
while( *str )
{
int next = -1;
if( 'a' <= *str && *str <= 'a'+base-10-1 )
next = *str - 'a' + 10;
}
+ //_SysDebug("strtoull - ret=0x%llx,next=%i,str='%s'", ret, next, str);
if( next < 0 )
break;
+
+ // If we're already out of range, keep eating
+ if( ret == ULLONG_MAX ) {
+ errno = ERANGE;
+ str ++;
+ // Keep eating until first unrecognised character
+ continue;
+ }
+
+ // Rough then accurate check against max value
+ if( ret >= max_before_ullong_max )
+ {
+ //_SysDebug("strtoull - 0x%llx>0x%llx", ret, max_before_ullong_max);
+ if( (ret - max_before_ullong_max) * base + next > space_above ) {
+ //_SysDebug("strtoull - %u*%u+%u (%u) > %u",
+ // (unsigned int)(ret - max_before_ullong_max), base, next, space_above);
+ ret = ULLONG_MAX;
+ errno = ERANGE;
+ str ++;
+ continue;
+ }
+ }
ret *= base;
ret += next;
str ++;
long long strtoll(const char *str, char **end, int base)
{
int neg = 0;
- unsigned long long ret;
if( !str ) {
errno = EINVAL;
str++;
// Check for negative (or positive) sign
- if(*str == '-' || *str == '+') {
+ if(*str == '-' || *str == '+')
+ {
+ //_SysDebug("strtoll - str[0:1] = '%.2s'", str);
+ if( !isdigit(str[1]) ) {
+ // Non-digit, invalid string
+ if(end) *end = (char*)str;
+ return 0;
+ }
neg = (*str == '-');
str++;
}
- ret = strtoull(str, end, base);
+ unsigned long long ret = strtoull(str, end, base);
+ //_SysDebug("strtoll - neg=%i,ret=%llu", neg, ret);
- if( neg )
+ if( neg ) {
+ // Abuses unsigned integer overflow
+ if( ret + LLONG_MIN < ret ) {
+ errno = ERANGE;
+ return LLONG_MIN;
+ }
return -ret;
+ }
else
+ {
+ if( ret > LLONG_MAX ) {
+ errno = ERANGE;
+ return LLONG_MAX;
+ }
return ret;
+ }
}
long strtol(const char *str, char **end, int base)
extern "C" {
#endif
+typedef float float_t;
+typedef double double_t;
+
+
+#define INFINITY (*(float*)((uint32_t[]){0x78000000}))
+#define NAN (*(float*)((uint32_t[]){0x78000001}))
+
extern double pow(double x, double y);
extern double exp(double x);
extern double log(double val);
OBJ = main.o unistd.o dirent.o stat.o utmpx.o termios.o\r
OBJ += pwd.o syslog.o sys_time.o sys_ioctl.o sys_resource.o\r
OBJ += fcntl.o clocks.o sys_wait.o unistd_crypt.o\r
-OBJ += grp.o pty.o mktemp.o utime.o\r
+OBJ += grp.o pty.o mktemp.o utime.o getopt.o\r
DEPFILES := $(OBJ:%.o=%.d)\r
BIN = libposix.so\r
\r
--- /dev/null
+/*
+ * Acess2 POSIX Emulation Layer
+ * - By John Hodge (thePowersGang)
+ *
+ * getopt.c
+ * - getopt() command line parsing code
+ */
+#include <getopt.h>
+
+// === GLOBALS ===
+char* optarg;
+ int opterr;
+ int optind;
+ int optopt;
+
+// === CODE ===
+int getopt(int argc, char * const argv[], const char *optstring)
+{
+ return -1;
+}
+
--- /dev/null
+/*
+ */
+#ifndef _LIBPOSIX_ENDIAN_H_
+#define _LIBPOSIX_ENDIAN_H_
+
+#define __LITTLE_ENDIAN 0
+#define __BIG_ENDIAN 0
+#define __BYTE_ORDER __LITTLE_ENDIAN
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 POSIX Emulation Layer
+ * - By John Hodge (thePowersGang)
+ *
+ * getopt.h
+ * - getopt() command line parsing code
+ */
+#ifndef _LIBPOSIX_GETOPT_H_
+#define _LIBPOSIX_GETOPT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern char* optarg;
+extern int opterr;
+extern int optind;
+extern int optopt;
+
+extern int getopt(int argc, char * const argv[], const char *optstring);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 POSIX Emulation Library
+ * - By John Hodge (thePowersGang)
+ *
+ * regex.h
+ * - POSIX regular expression support
+ */
+#ifndef _LIBPOSIX_REGEX_H_
+#define _LIBPOSIX_REGEX_H_
+
+typedef struct {
+ void *unused;
+} regex_t;
+
+typedef size_t regoff_t;
+
+typedef struct {
+ regoff_t rm_so;
+ regoff_t rm_eo;
+} regmatch_t;
+
+extern int regcomp(regex_t *preg, const char *regex, int cflags);
+extern int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
+extern size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
+extern void regfree(regex_t *preg);
+
+enum {
+ REG_BADBR = 1,
+ REG_BADPAT,
+ REG_BADRPT,
+};
+
+
+#endif
+
+
--- /dev/null
+/*
+ * Acess2 POSIX Emulation Library
+ * - By John Hodge (thePowersGang)
+ *
+ * strings.h
+ * - BSD's verison of string.h
+ */
+#ifndef _LIBPOSIX_STRINGS_H_
+#define _LIBPOSIX_STRINGS_H_
+
+
+
+#endif
+
extern int unlink(const char *pathname);
+#define F_OK 00
+#define R_OK 04
+#define W_OK 02
+#define X_OK 01
extern int access(const char *pathname, int mode);
extern pid_t setsid(void);
extern char *crypt(const char *key, const char *salt);
// - pty.c
+extern int isatty(int fd);
extern char *ttyname(int fd);
extern int ttyname_r(int fd, char *buf, size_t buflen);
return ENOTIMPL;
}
+
+int isatty(int fd)
+{
+ if( fd < 0 ) {
+ errno = EBADF;
+ return 0;
+ }
+
+ int type = _SysIOCtl(fd, DRV_IOCTL_TYPE, NULL);
+ if( type == -1 )
+ return 0;
+ if( type != DRV_TYPE_TERMINAL ) {
+ errno = ENOTTY;
+ // NOTE: Pre POSIX 2001, EINVAL was returned
+ return 0;
+ }
+ return 1;
+}