;name: isatty.asm
;description: check if a user session is started in X-server, console on X-server or
;             a "real" (what's real?) terminal.
;build: nasm -felf64 -o isatty.o isatty.asm
;       ld -o isatty -m elf_x86_64 -lc --dynamic-linker /lib64/ld-linux-x86-64.so.2 isatty.o `pkg-config --libs gtk+-3.0`
;agguro (c), 17 april 2021

bits 64

[list -]
    %include "unistd.inc"
    %include "sys/termios.inc"
    %define    GTK_WINDOW_TOPLEVEL   0
    %define    GTK_WIN_POS_CENTER    1
    %define    FALSE                 0
    %define    TRUE                  1
    extern     gtk_init
    extern     gtk_main
    extern     gtk_main_quit
    extern     gtk_widget_show
    extern     gtk_window_new
    extern     gtk_window_set_title
    extern     g_signal_connect_data
    extern     exit
[list +]

global _start

section .bss
;uninitialized read-write data
    envplist:   resq    0
    window:     resq    0

section .data
    TERMIOS	termios                         ;termios structure
section .rodata
    szTitle:        db  "isatty from GUI",0
    szDestroy:      db  "destroy",0
    szXtermSess:    db  "isatty from a terminal session on X-server", 10
    .len:           equ $-szXtermSess
    szRealterm:     db  "isatty from a 'real' terminal", 10
    .len:           equ $-szRealterm

section .text

    ;get the enviroment parameterlist pointer
    mov     rax,rsp                         ;address of argc
    mov     rdx,[rsp]                       ;argc
    inc     rdx                             ;for programname
    inc     rdx                             ;null pointer
    shl     rdx,3                           ;argc*8,stackdisplacement for arguments and ending NULL pointer
    add     rax,rdx                         ;envplist
    mov     qword [envplist],rax            ;save start of environment parameter list
    ;check if the application is ran from a terminal session
    syscall ioctl,stdin,TCGETS,termios
    ;if the result in rax is a negative value then we aren't running from a terminal
    test    rax,rax
    js      .gui
    ;if rax returns zero the program is running from a terminal
    ;we have to figure out if the terminal is started from a desktop (GUI) or not
    ;this can be done by looking for the environment parameter TERM=linux.
    mov     rax,qword [envplist]            ;read start of environment parameter list
    mov     r15,qword [rax]                 ;read address of environment parameter
    test    r15,r15                         ;check if at end of list
    jz      .endoflist
    mov     edx,dword [r15]                 ;read first dword
    cmp     edx,"TERM"
    je      .foundkeyword
    jmp     .next
    mov     dl,byte [r15+4]
    cmp     dl,"="
    jne     .next
    mov     rdx,qword [r15+5]
    shl     rdx,24
    shr     rdx,24
    mov     rcx,"linux"
    xor     rdx,rcx
    test    rdx,rdx
    jz      .realterminal
    add     rax,8                           ;next environment parameter
    jmp     .repeat

    ;if we arrive here we are in a GUI terminal
    syscall write,stdout,szXtermSess,szXtermSess.len
    jmp     .done

    ;if we are here we are in a real terminal, just to make the difference we show
    ;another message (but it can be the same or just the application that starts)
    syscall write,stdout,szRealterm,szRealterm.len

    ;that's it, end program
    syscall exit,0

    xor     rdi,rdi                 
    xor     rsi,rsi
    call    gtk_init
    ;if initialization doesn't succeed then the application
    ;terminates.  If that is unwanted behaviour then we need
    ;to use gtk_init_check instead.
    ;create a new window
    mov     rdi,GTK_WINDOW_TOPLEVEL
    call    gtk_window_new
    mov     r15,rax                     ;save gtkwindow pointer
    ;set the title
    mov     rdi,rax
    mov     rsi,szTitle
    call    gtk_window_set_title
    ;connect the destroy signal to gtk_main_quit event handler
    xor     r9d,r9d                    ;combination of GConnectFlags 
    xor     r8d,r8d                    ;a GClosureNotify for data
    xor     rcx,rcx                    ;pointer to the data to pass
    mov     rdx,gtk_main_quit          ;pointer to the handler
    mov     rsi,szDestroy              ;pointer to the signal
    mov     rdi,r15                    ;pointer to the widget instance
    ;C programs uses g_signal_connect, this is not a library
    call    g_signal_connect_data
    ;show the window
    mov     rdi,r15
    call    gtk_widget_show
    ;go into applications main loop
    call    gtk_main
    ;exit program
    xor     rdi,rdi
    call    exit