table: TypeCategory; freetype: remove C code; [typedef] C structs;

Objective-C interfaces
pull/1714/head
Alexander Medvednikov 2019-08-20 14:34:29 +03:00
parent c8da79d785
commit eb5fcafa77
8 changed files with 247 additions and 213 deletions

View File

@ -13,7 +13,7 @@ CommonCHeaders = '
#define STRUCT_DEFAULT_VALUE {} #define STRUCT_DEFAULT_VALUE {}
#define EMPTY_STRUCT_DECLARATION #define EMPTY_STRUCT_DECLARATION
#define EMPTY_STRUCT_INIT #define EMPTY_STRUCT_INIT 0
#define OPTION_CAST(x) (x) #define OPTION_CAST(x) (x)
#ifdef _WIN32 #ifdef _WIN32
@ -105,4 +105,4 @@ void init_consts();
' '
) )

View File

@ -140,7 +140,7 @@ fn (p mut Parser) fn_decl() {
} }
receiver_typ = p.get_type() receiver_typ = p.get_type()
T := p.table.find_type(receiver_typ) T := p.table.find_type(receiver_typ)
if T.is_interface { if T.cat == .interface_ {
p.error('invalid receiver type `$receiver_typ` (`$receiver_typ` is an interface)') p.error('invalid receiver type `$receiver_typ` (`$receiver_typ` is an interface)')
} }
// Don't allow modifying types from a different module // Don't allow modifying types from a different module

View File

@ -74,6 +74,8 @@ mut:
returns bool returns bool
vroot string vroot string
is_c_struct_init bool is_c_struct_init bool
is_empty_c_struct_init bool
is_c_fn_call bool
can_chash bool can_chash bool
attr string attr string
v_script bool // "V bash", import all os functions into global space v_script bool // "V bash", import all os functions into global space
@ -177,7 +179,7 @@ fn (p mut Parser) parse() {
} }
p.fgenln('\n') p.fgenln('\n')
p.builtin_mod = p.mod == 'builtin' p.builtin_mod = p.mod == 'builtin'
p.can_chash = p.mod == 'freetype' || p.mod=='ui' // TODO tmp remove p.can_chash = p.mod=='ui' || p.mod == 'darwin'// TODO tmp remove
// Import pass - the first and the smallest pass that only analyzes imports // Import pass - the first and the smallest pass that only analyzes imports
// fully qualify the module name, eg base64 to encoding.base64 // fully qualify the module name, eg base64 to encoding.base64
fq_mod := p.table.qualify_module(p.mod, p.file_path) fq_mod := p.table.qualify_module(p.mod, p.file_path)
@ -199,7 +201,7 @@ fn (p mut Parser) parse() {
// Go through every top level token or throw a compilation error if a non-top level token is met // Go through every top level token or throw a compilation error if a non-top level token is met
for { for {
switch p.tok { switch p.tok {
case Token.key_import: case .key_import:
if p.peek() == .key_const { if p.peek() == .key_const {
p.const_decl() p.const_decl()
} }
@ -483,26 +485,27 @@ fn (p mut Parser) interface_method(field_name, receiver string) &Fn {
return method return method
} }
fn key_to_type_cat(tok Token) TypeCategory {
switch tok {
case Token.key_interface: return TypeCategory.interface_
case Token.key_struct: return TypeCategory.struct_
case Token.key_union: return TypeCategory.union_
//Token.key_ => return .interface_
}
panic('')
}
// also unions and interfaces // also unions and interfaces
fn (p mut Parser) struct_decl() { fn (p mut Parser) struct_decl() {
// Attribute before type? // V can generate Objective C for integration with Cocoa
mut objc_parent := '' // `[interface:ParentInterface]`
mut is_objc := false// V can generate Objective C for integration with Cocoa is_objc := p.attr.starts_with('interface')
// [attr] objc_parent := if is_objc { p.attr.right(10) } else { '' }
if p.tok == .lsbr { // interface, union, struct
p.check(.lsbr)
// `[interface:ParentInterface]`
is_objc = p.tok == .key_interface
p.next()
if is_objc {
p.check(.colon)
objc_parent = p.check_name()
}
p.check(.rsbr)
}
is_interface := p.tok == .key_interface is_interface := p.tok == .key_interface
is_union := p.tok == .key_union is_union := p.tok == .key_union
is_struct := p.tok == .key_struct is_struct := p.tok == .key_struct
mut cat := key_to_type_cat(p.tok)
p.fgen(p.tok.str() + ' ') p.fgen(p.tok.str() + ' ')
// Get type name // Get type name
p.next() p.next()
@ -517,6 +520,10 @@ fn (p mut Parser) struct_decl() {
if is_c { if is_c {
p.check(.dot) p.check(.dot)
name = p.check_name() name = p.check_name()
cat = .c_struct
if p.attr == 'typedef' {
cat = .c_typedef
}
} }
if !is_c && !good_type_name(name) { if !is_c && !good_type_name(name) {
p.error('bad struct name, e.g. use `HttpRequest` instead of `HTTPRequest`') p.error('bad struct name, e.g. use `HttpRequest` instead of `HTTPRequest`')
@ -549,13 +556,14 @@ fn (p mut Parser) struct_decl() {
typ.mod = p.mod typ.mod = p.mod
typ.is_c = is_c typ.is_c = is_c
typ.is_placeholder = false typ.is_placeholder = false
typ.cat = cat
} }
else { else {
typ = &Type { typ = &Type {
name: name name: name
mod: p.mod mod: p.mod
is_c: is_c is_c: is_c
is_interface: is_interface cat: cat
} }
} }
// Struct `C.Foo` declaration, no body // Struct `C.Foo` declaration, no body
@ -706,7 +714,7 @@ fn (p mut Parser) enum_decl(_enum_name string) {
name: enum_name name: enum_name
mod: p.mod mod: p.mod
parent: 'int' parent: 'int'
is_enum: true cat: TypeCategory.enum_
enum_vals: fields enum_vals: fields
}) })
p.check(.rcbr) p.check(.rcbr)
@ -905,7 +913,8 @@ fn (p mut Parser) get_type() string {
} }
typ += p.lit typ += p.lit
if !p.is_struct_init { if !p.is_struct_init {
// Otherwise we get `foo := FooFoo{` because `Foo` was already generated in name_expr() // Otherwise we get `foo := FooFoo{` because `Foo` was already
// generated in name_expr()
p.fgen(p.lit) p.fgen(p.lit)
} }
// C.Struct import // C.Struct import
@ -1120,7 +1129,6 @@ fn (p mut Parser) statement(add_semi bool) string {
} }
// `a := 777` // `a := 777`
else if p.peek() == .decl_assign { else if p.peek() == .decl_assign {
p.log('var decl')
p.var_decl() p.var_decl()
} }
else { else {
@ -1128,7 +1136,7 @@ fn (p mut Parser) statement(add_semi bool) string {
if p.lit == 'panic' || p.lit == 'exit' { if p.lit == 'panic' || p.lit == 'exit' {
p.returns = true p.returns = true
} }
// `a + 3`, `a(7)` or maybe just `a` // `a + 3`, `a(7)`, or just `a`
q = p.bool_expression() q = p.bool_expression()
} }
case Token.key_goto: case Token.key_goto:
@ -1220,7 +1228,7 @@ fn ($v.name mut $v.typ) $p.cur_fn.name (...) {
is_str := v.typ == 'string' is_str := v.typ == 'string'
switch tok { switch tok {
case Token.assign: case Token.assign:
if !is_map { if !is_map && !p.is_empty_c_struct_init {
p.gen(' = ') p.gen(' = ')
} }
case Token.plus_assign: case Token.plus_assign:
@ -1326,13 +1334,18 @@ fn (p mut Parser) var_decl() {
}) })
if !or_else { if !or_else {
gen_name := p.table.var_cgen_name(name) gen_name := p.table.var_cgen_name(name)
mut nt_gen := p.table.cgen_name_type_pair(gen_name, typ) + '=' mut nt_gen := p.table.cgen_name_type_pair(gen_name, typ)
// `foo := C.Foo{}` => `Foo foo;`
if !p.is_empty_c_struct_init {
nt_gen += '='
}
if is_static { if is_static {
nt_gen = 'static $nt_gen' nt_gen = 'static $nt_gen'
} }
p.cgen.set_placeholder(pos, nt_gen) p.cgen.set_placeholder(pos, nt_gen)
} }
p.var_decl_name = '' p.var_decl_name = ''
p.is_empty_c_struct_init = false
} }
const ( const (
@ -1383,7 +1396,7 @@ fn (p mut Parser) bterm() string {
is_str := typ=='string' && !p.is_sql is_str := typ=='string' && !p.is_sql
tok := p.tok tok := p.tok
// if tok in [ .eq, .gt, .lt, .le, .ge, .ne] { // if tok in [ .eq, .gt, .lt, .le, .ge, .ne] {
if tok == .eq || (tok == .assign && p.is_sql) || tok == .gt || tok == .lt || tok == .le || tok == .ge || tok == .ne { if tok == .eq || tok == .gt || tok == .lt || tok == .le || tok == .ge || tok == .ne {
p.fgen(' ${p.tok.str()} ') p.fgen(' ${p.tok.str()} ')
if is_str { if is_str {
p.gen(',') p.gen(',')
@ -1466,7 +1479,7 @@ fn (p mut Parser) name_expr() string {
if p.tok == .dot { if p.tok == .dot {
//println('got enum dot val $p.left_type pass=$p.pass $p.scanner.line_nr left=$p.left_type') //println('got enum dot val $p.left_type pass=$p.pass $p.scanner.line_nr left=$p.left_type')
T := p.find_type(p.expected_type) T := p.find_type(p.expected_type)
if T.is_enum { if T.cat == .enum_ {
p.check(.dot) p.check(.dot)
val := p.check_name() val := p.check_name()
// Make sure this enum value exists // Make sure this enum value exists
@ -1552,7 +1565,7 @@ fn (p mut Parser) name_expr() string {
// Color.green // Color.green
else if p.peek() == .dot { else if p.peek() == .dot {
enum_type := p.table.find_type(name) enum_type := p.table.find_type(name)
if !enum_type.is_enum { if enum_type.cat != .enum_ {
p.error('`$name` is not an enum') p.error('`$name` is not an enum')
} }
p.next() p.next()
@ -1563,19 +1576,8 @@ fn (p mut Parser) name_expr() string {
p.next() p.next()
return enum_type.name return enum_type.name
} }
// struct initialization
else if p.peek() == .lcbr { else if p.peek() == .lcbr {
// go back to name start (mod.name)
/*
p.scanner.pos = hack_pos
p.tok = hack_tok
p.lit = hack_lit
*/
// TODO hack. If it's a C type, we may need to add struct before declaration:
// a := &C.A{} ==> struct A* a = malloc(sizeof(struct A));
if is_c_struct_init {
p.is_c_struct_init = true
p.cgen.insert_before('struct /*c struct init*/')
}
if ptr { if ptr {
name += '*' // `&User{}` => type `User*` name += '*' // `&User{}` => type `User*`
} }
@ -1597,12 +1599,15 @@ fn (p mut Parser) name_expr() string {
name: name// .replace('c_', '') name: name// .replace('c_', '')
is_c: true is_c: true
} }
p.is_c_fn_call = true
p.fn_call(f, 0, '', '') p.fn_call(f, 0, '', '')
p.is_c_fn_call = false
// Try looking it up. Maybe its defined with "C.fn_name() fn_type", // Try looking it up. Maybe its defined with "C.fn_name() fn_type",
// then we know what type it returns // then we know what type it returns
cfn := p.table.find_fn(name) cfn := p.table.find_fn(name)
// Not Found? Return 'void*' // Not Found? Return 'void*'
if cfn.name == '' { if cfn.name == '' {
//return 'cvoid' //'void*'
return 'void*' return 'void*'
} }
return cfn.typ return cfn.typ
@ -1791,7 +1796,7 @@ fn (p mut Parser) dot(str_typ string, method_ph int) string {
p.gen_array_str(mut typ) p.gen_array_str(mut typ)
has_method = true has_method = true
} }
if !typ.is_c && !has_field && !has_method && !p.first_pass() { if !typ.is_c && !p.is_c_fn_call && !has_field && !has_method && !p.first_pass() {
if typ.name.starts_with('Option_') { if typ.name.starts_with('Option_') {
opt_type := typ.name.right(7) opt_type := typ.name.right(7)
p.error('unhandled option type: $opt_type?') p.error('unhandled option type: $opt_type?')
@ -1809,7 +1814,7 @@ fn (p mut Parser) dot(str_typ string, method_ph int) string {
p.error('type `$typ.name` has no field or method `$field_name`') p.error('type `$typ.name` has no field or method `$field_name`')
} }
mut dot := '.' mut dot := '.'
if str_typ.contains('*') { if str_typ.ends_with('*') || str_typ == 'FT_Face' { // TODO fix C ptr typedefs
dot = '->' dot = '->'
} }
// field // field
@ -2113,7 +2118,7 @@ fn (p mut Parser) expression() string {
p.check_space(.left_shift) p.check_space(.left_shift)
// Get the value we are pushing // Get the value we are pushing
p.gen(', (') p.gen(', (')
// Imkey_mut? Can we push? // Immutable? Can we push?
if !p.expr_var.is_mut && !p.pref.translated { if !p.expr_var.is_mut && !p.pref.translated {
p.error('`$p.expr_var.name` is immutable (can\'t <<)') p.error('`$p.expr_var.name` is immutable (can\'t <<)')
} }
@ -2130,7 +2135,7 @@ fn (p mut Parser) expression() string {
p.check_types(expr_type, tmp_typ) p.check_types(expr_type, tmp_typ)
// Pass tmp var info to the _PUSH macro // Pass tmp var info to the _PUSH macro
// Prepend tmp initialisation and push call // Prepend tmp initialisation and push call
// Don't dereference if it's already a key_mut array argument (`fn foo(mut []int)`) // Don't dereference if it's already a mutable array argument (`fn foo(mut []int)`)
push_call := if typ.contains('*'){'_PUSH('} else { '_PUSH(&'} push_call := if typ.contains('*'){'_PUSH('} else { '_PUSH(&'}
p.cgen.set_placeholder(ph, push_call) p.cgen.set_placeholder(ph, push_call)
p.gen('), $tmp, $tmp_typ)') p.gen('), $tmp, $tmp_typ)')
@ -2791,18 +2796,32 @@ fn (p mut Parser) array_init() string {
fn (p mut Parser) struct_init(typ string, is_c_struct_init bool) string { fn (p mut Parser) struct_init(typ string, is_c_struct_init bool) string {
p.is_struct_init = true p.is_struct_init = true
t := p.table.find_type(typ)
// TODO hack. If it's a C type, we may need to add struct before declaration:
// a := &C.A{} ==> struct A* a = malloc(sizeof(struct A));
if is_c_struct_init { // && t.cat != .c_typedef {
p.is_c_struct_init = true
if t.cat != .c_typedef {
p.cgen.insert_before('struct /*c struct init*/')
}
}
p.next() p.next()
p.scanner.fmt_out.cut(typ.len) p.scanner.fmt_out.cut(typ.len)
ptr := typ.contains('*') ptr := typ.contains('*')
// TODO tm struct struct bug // TODO tm struct struct bug
if typ == 'tm' { if typ == 'tm' {
p.cgen.lines[p.cgen.lines.len-1] = '' p.cgen.lines[p.cgen.lines.len-1] = ''
p.cgen.lines[p.cgen.lines.len-2] = ''
} }
p.check(.lcbr) p.check(.lcbr)
// tmp := p.get_tmp() // `user := User{foo:bar}` => `User user = (User){ .foo = bar}`
if !ptr { if !ptr {
if p.is_c_struct_init { if p.is_c_struct_init {
// `face := C.FT_Face{}` => `FT_Face face;`
if p.tok == .rcbr {
p.is_empty_c_struct_init = true
p.check(.rcbr)
return typ
}
p.gen('(struct $typ) {') p.gen('(struct $typ) {')
p.is_c_struct_init = false p.is_c_struct_init = false
} }
@ -2829,7 +2848,6 @@ fn (p mut Parser) struct_init(typ string, is_c_struct_init bool) string {
mut inited_fields := []string mut inited_fields := []string
peek := p.peek() peek := p.peek()
if peek == .colon || p.tok == .rcbr { if peek == .colon || p.tok == .rcbr {
t := p.table.find_type(typ)
for p.tok != .rcbr { for p.tok != .rcbr {
field := p.check_name() field := p.check_name()
if !t.has_field(field) { if !t.has_field(field) {
@ -2942,6 +2960,9 @@ fn (p mut Parser) cast(typ string) string {
p.check(.lpar) p.check(.lpar)
p.expected_type = typ p.expected_type = typ
expr_typ := p.bool_expression() expr_typ := p.bool_expression()
// `face := FT_Face(cobj)` => `FT_Face face = *((FT_Face*)cobj);`
casting_voidptr_to_value := expr_typ == 'void*' && typ != 'int' &&
typ != 'byteptr' && !typ.ends_with('*')
p.expected_type = '' p.expected_type = ''
// `string(buffer)` => `tos2(buffer)` // `string(buffer)` => `tos2(buffer)`
// `string(buffer, len)` => `tos(buffer, len)` // `string(buffer, len)` => `tos(buffer, len)`
@ -2976,6 +2997,9 @@ fn (p mut Parser) cast(typ string) string {
else if typ == 'byte' && expr_typ == 'string' { else if typ == 'byte' && expr_typ == 'string' {
p.error('cannot cast `$expr_typ` to `$typ`, use backquotes `` to create a `$typ` or access the value of an index of `$expr_typ` using []') p.error('cannot cast `$expr_typ` to `$typ`, use backquotes `` to create a `$typ` or access the value of an index of `$expr_typ` using []')
} }
else if casting_voidptr_to_value {
p.cgen.set_placeholder(pos, '*($typ*)(')
}
else { else {
p.cgen.set_placeholder(pos, '($typ)(') p.cgen.set_placeholder(pos, '($typ)(')
} }
@ -3524,7 +3548,13 @@ fn (p &Parser) building_v() bool {
fn (p mut Parser) attribute() { fn (p mut Parser) attribute() {
p.check(.lsbr) p.check(.lsbr)
p.attr = p.check_name() if p.tok == .key_interface {
p.check(.key_interface)
p.check(.colon)
p.attr = 'interface:' + p.check_name()
} else {
p.attr = p.check_name()
}
p.check(.rsbr) p.check(.rsbr)
if p.tok == .func { if p.tok == .func {
p.fn_decl() p.fn_decl()

View File

@ -43,17 +43,26 @@ enum AccessMod {
public_mut_mut // public and mutable both inside and outside (not recommended to use, that's why it's so verbose) public_mut_mut // public and mutable both inside and outside (not recommended to use, that's why it's so verbose)
} }
enum TypeCategory {
struct_
func
interface_ // 2
enum_
union_
c_struct // 5
c_typedef
}
struct Type { struct Type {
mut: mut:
mod string mod string
name string name string
cat TypeCategory
fields []Var fields []Var
methods []Fn methods []Fn
parent string parent string
func Fn // For cat == FN (type myfn fn()) func Fn // For cat == FN (type myfn fn())
is_c bool // C.FI.le is_c bool // `C.FILE`
is_interface bool
is_enum bool
enum_vals []string enum_vals []string
gen_types []string gen_types []string
// This field is used for types that are not defined yet but are known to exist. // This field is used for types that are not defined yet but are known to exist.
@ -488,7 +497,7 @@ fn (p mut Parser) _check_types(got_, expected_ string, throw bool) bool {
return true return true
} }
// Todo void* allows everything right now // Todo void* allows everything right now
if got=='void*' || expected=='void*' { if got=='void*' || expected=='void*' {// || got == 'cvoid' || expected == 'cvoid' {
// if !p.builtin_mod { // if !p.builtin_mod {
if p.pref.is_play { if p.pref.is_play {
return false return false
@ -623,7 +632,7 @@ fn type_default(typ string) string {
// TODO PERF O(n) // TODO PERF O(n)
fn (t &Table) is_interface(name string) bool { fn (t &Table) is_interface(name string) bool {
for typ in t.types { for typ in t.types {
if typ.is_interface && typ.name == name { if typ.cat == .interface_ && typ.name == name {
return true return true
} }
} }
@ -757,7 +766,7 @@ fn (t mut Table) register_generic_fn_type(fn_name, typ string) {
fn (p mut Parser) typ_to_fmt(typ string, level int) string { fn (p mut Parser) typ_to_fmt(typ string, level int) string {
t := p.table.find_type(typ) t := p.table.find_type(typ)
if t.is_enum { if t.cat == .enum_ {
return '%d' return '%d'
} }
switch typ { switch typ {

View File

@ -195,7 +195,7 @@ pub fn (n int) hex() string {
} else { } else {
11 11
} }
hex := malloc(len) // 0x + \n hex := malloc(len) // 0x + \n
count := int(C.sprintf(hex, '0x%x', n)) count := int(C.sprintf(hex, '0x%x', n))
return tos(hex, count) return tos(hex, count)
} }
@ -245,14 +245,14 @@ pub fn (c byte) str() string {
} }
pub fn (c byte) is_capital() bool { pub fn (c byte) is_capital() bool {
return c >= `A` && c <= `Z` return c >= `A` && c <= `Z`
} }
pub fn (b []byte) clone() []byte { pub fn (b []byte) clone() []byte {
mut res := [byte(0); b.len] mut res := [byte(0); b.len]
for i := 0; i < b.len; i++ { for i := 0; i < b.len; i++ {
res[i] = b[i] res[i] = b[i]
} }
return res return res
} }

View File

@ -0,0 +1,22 @@
module darwin
#include <Cocoa/Cocoa.h>
#flag -framework Cocoa
struct C.NSString { }
// macOS and iOS helpers
fn nsstring(s string) *NSString {
// #return @"" ;
// println('ns $s len=$s.len')
# return [ [ NSString alloc ] initWithBytesNoCopy:s.str length:s.len
# encoding:NSUTF8StringEncoding freeWhenDone: false];
return 0
//ns := C.alloc_NSString()
//return ns.initWithBytesNoCopy(s.str, length: s.len,
//encoding: NSUTF8StringEncoding, freeWhenDone: false)
}

View File

@ -2,25 +2,25 @@
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module freetype module freetype
import ( import (
os os
gx gx
gg gg
stbi stbi
glm glm
gl gl
) )
#flag darwin -I/usr/local/include/freetype2 #flag darwin -I/usr/local/include/freetype2
#flag darwin -I/opt/local/include/freetype2 #flag darwin -I/opt/local/include/freetype2
#flag -lfreetype #flag -lfreetype
//#flag -I @VROOT/thirdparty/freetype //#flag -I @VROOT/thirdparty/freetype
//#flag @VROOT/thirdparty/freetype/libfreetype.a //#flag @VROOT/thirdparty/freetype/libfreetype.a
#flag darwin -lpng -lbz2 -lz #flag darwin -lpng -lbz2 -lz
#flag linux -I/usr/include/freetype2 #flag linux -I/usr/include/freetype2
@ -30,6 +30,8 @@ import (
#include "ft2build.h" #include "ft2build.h"
#include FT_FREETYPE_H #include FT_FREETYPE_H
const ( const (
DEFAULT_FONT_SIZE = 12 DEFAULT_FONT_SIZE = 12
) )
@ -45,6 +47,11 @@ struct Face {
cobj voidptr cobj voidptr
} }
[typedef]
struct C.FT_Library {
}
struct Context { struct Context {
shader gl.Shader shader gl.Shader
// use_ortho bool // use_ortho bool
@ -63,67 +70,67 @@ struct Context {
scale int // retina = 2 , normal = 1 scale int // retina = 2 , normal = 1
} }
/* struct C.Bitmap {
struct Cfg { width int
width int rows int
height int buffer int
use_ortho bool
retina bool
scale int
font_size int
create_window bool
window_user_ptr voidptr
window_title string
always_on_top bool
} }
*/
struct C.Advance {
x int
}
struct C.Glyph {
bitmap Bitmap
bitmap_left int
bitmap_top int
advance Advance
}
[typedef]
struct C.FT_Face {
glyph *Glyph
}
// jfn ft_load_char(face FT_Face, code FT_ULong) Character {
// fn ft_load_char(_face voidptr, _code voidptr) Character {
fn ft_load_char(_face Face, code i64) Character { fn ft_load_char(_face Face, code i64) Character {
// #FT_Face face = *(FT_Face*)(_face); FT_ULong code = *(FT_ULong*)(code); //println('ftload_char( code=$code)')
# FT_Face face = *((FT_Face*)_face.cobj); //C.printf('face=%p\n', _face)
# int condition = FT_Load_Char(face, code, FT_LOAD_RENDER); face := FT_Face(_face.cobj)
if (C.condition != 0) ret := int(C.FT_Load_Char(face, code, C.FT_LOAD_RENDER))
{ if ret != 0 {
println('freetype: Failed to load Glyph') println('freetype: failed to load glyph (utf32 code=$code, ' +
exit(1) 'error code=$ret)')
return Character{}
} }
// Generate texture // Generate texture
# GLuint texture; mut texture := 0
# glGenTextures(1, &texture); C.glGenTextures(1, &texture)
# glBindTexture(GL_TEXTURE_2D, texture); C.glBindTexture(C.GL_TEXTURE_2D, texture)
# glTexImage2D( fgwidth := face.glyph.bitmap.width
# GL_TEXTURE_2D, fgrows := face.glyph.bitmap.rows
# 0, C.glTexImage2D(C.GL_TEXTURE_2D, 0, C.GL_RED, fgwidth, fgrows,
# GL_RED, 0, C.GL_RED, C.GL_UNSIGNED_BYTE, face.glyph.bitmap.buffer)
# face->glyph->bitmap.width,
# face->glyph->bitmap.rows,
# 0,
# GL_RED,
# GL_UNSIGNED_BYTE,
# face->glyph->bitmap.buffer
# );
// Set texture options // Set texture options
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); C.glTexParameteri(GL_TEXTURE_2D, C.GL_TEXTURE_WRAP_S, C.GL_CLAMP_TO_EDGE)
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); C.glTexParameteri(GL_TEXTURE_2D, C.GL_TEXTURE_WRAP_T, C.GL_CLAMP_TO_EDGE)
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); C.glTexParameteri(GL_TEXTURE_2D, C.GL_TEXTURE_MIN_FILTER, C.GL_LINEAR)
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); C.glTexParameteri(C.GL_TEXTURE_2D, C.GL_TEXTURE_MAG_FILTER, C.GL_LINEAR)
// Now store character for later use fgleft := face.glyph.bitmap_left
ch := Character{} fgtop := face.glyph.bitmap_top
# ch.texture_id=texture ; // Create the character
# ch.size = gg__vec2(face->glyph->bitmap.width, face->glyph->bitmap.rows); return Character {
# ch.bearing = gg__vec2(face->glyph->bitmap_left, face->glyph->bitmap_top), texture_id: u32(texture)
# ch.advance = face->glyph->advance.x; size: gg.vec2(int(u32(fgwidth)), int(u32(fgrows)))
return ch bearing: gg.vec2(int(u32(fgleft)), int(u32(fgtop)))
advance: (u32(face.glyph.advance.x))
}
} }
pub fn new_context(cfg gg.Cfg) *Context { pub fn new_context(cfg gg.Cfg) *Context {
scale := cfg.scale scale := cfg.scale
// Can only have text in ortho mode // Can only have text in ortho mode
if !cfg.use_ortho { if !cfg.use_ortho {
return &Context{} return &Context{}
} }
mut width := cfg.width * scale mut width := cfg.width * scale
mut height := cfg.height * scale mut height := cfg.height * scale
@ -135,27 +142,25 @@ pub fn new_context(cfg gg.Cfg) *Context {
// height = height * 2// scale// 2 // height = height * 2// scale// 2
// font_size *= scale// 2 // font_size *= scale// 2
// } // }
/* /*
gl.viewport(0, 0, width, height) gl.viewport(0, 0, width, height)
*/ */
// gl.enable(GL_CULL_FACE) // TODO NEED CULL? // gl.enable(GL_CULL_FACE) // TODO NEED CULL?
gl.enable(GL_BLEND) gl.enable(GL_BLEND)
//return &GG{} C.glBlendFunc(C.GL_SRC_ALPHA, C.GL_ONE_MINUS_SRC_ALPHA)
// return &GG{}
# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
shader := gl.new_shader('text') shader := gl.new_shader('text')
shader.use() shader.use()
projection := glm.ortho(0, width, 0, height)// 0 at BOT projection := glm.ortho(0, width, 0, height)// 0 at BOT
// projection_new := ortho(0, width, 0, height)// 0 at BOT
// projection := gl.ortho(0, width,height,0) // 0 at TOP
shader.set_mat4('projection', projection) shader.set_mat4('projection', projection)
// FREETYPE // FREETYPE
# FT_Library ft; ft := FT_Library{}
// All functions return a value different than 0 whenever an error occurred // All functions return a value different than 0 whenever
# if (FT_Init_FreeType(&ft)) // an error occurred
println('ERROR::FREETYPE: Could not init FreeType Library') mut ret := C.FT_Init_FreeType(&ft)
if ret != 0 {
panic('freetype: Could not init FreeType Library')
}
// Load font as face // Load font as face
// face := FT_Face{}
mut font_path := cfg.font_path mut font_path := cfg.font_path
if font_path == '' { if font_path == '' {
font_path = 'RobotoMono-Regular.ttf' font_path = 'RobotoMono-Regular.ttf'
@ -170,37 +175,27 @@ pub fn new_context(cfg gg.Cfg) *Context {
return 0 return 0
} }
println('Trying to load font from $font_path') println('Trying to load font from $font_path')
# FT_Face face; face := C.FT_Face{}
# int condition = FT_New_Face(ft, font_path.str, 0, &face); ret = int(C.FT_New_Face(ft, font_path.str, 0, &face))
if (C.condition != 0) if ret != 0 {
// # if (FT_New_Face(ft, "/Library/Fonts/Courier New.ttf", 0, &face)) println('freetype: failed to load the font (error=$ret)')
// # if (FT_New_Face(ft, "/System/Library/Fonts/Apple Color Emoji.ttc", 0, &face))
{
println('freetyp: Failed to load font')
exit(1) exit(1)
} }
// Set size to load glyphs as // Set size to load glyphs as
# FT_Set_Pixel_Sizes(face, 0, font_size) ; C.FT_Set_Pixel_Sizes(face, 0, font_size)
// Disable byte-alignment restriction // Disable byte-alignment restriction
# glPixelStorei(GL_UNPACK_ALIGNMENT, 1); C.glPixelStorei(C.GL_UNPACK_ALIGNMENT, 1)
// Gen texture // Gen texture
// Load first 128 characters of ASCII set // Load first 128 characters of ASCII set
mut chars := []Character{} mut chars := []Character{}
f := Face { f := Face {
cobj: 0 cobj: &face
} }
# f.cobj = &face;
// # for (GLubyte c = 0; c < 128; c++)
for c := 0; c < 128; c++ { for c := 0; c < 128; c++ {
// ch := Character{}
// ch:=ft_load_char(face, c)
// # ch =gg__ft_load_char(&face, &c);
// ////////////////////////////////
mut ch := ft_load_char(f, i64(c)) mut ch := ft_load_char(f, i64(c))
// s := utf32_to_str(uint(0x043f)) // s := utf32_to_str(uint(0x043f))
// s := 'п' // s := 'п'
// ch = ft_load_char(f, s.utf32_code()) // ch = ft_load_char(f, s.utf32_code())
// # ch = gg__ft_load_char(f, 0x043f); // RUS P
// # unsigned long c = FT_Get_Char_Index(face, 0x043f ); // # unsigned long c = FT_Get_Char_Index(face, 0x043f );
// # printf("!!!!!!!!! %lu\n", c); // # printf("!!!!!!!!! %lu\n", c);
// # c = FT_Get_Char_Index(face, 0xd0bf ); // # c = FT_Get_Char_Index(face, 0xd0bf );
@ -209,9 +204,6 @@ pub fn new_context(cfg gg.Cfg) *Context {
chars << ch chars << ch
} }
ch := Character{} ch := Character{}
// # ch = gg__ft_load_char(f, 0x0000043f);
// # ch = gg__ft_load_char(f, 128169);
// chars.push(ch)
// Configure VAO // Configure VAO
vao := gl.gen_vertex_array() vao := gl.gen_vertex_array()
println('new gg text context vao=$vao') println('new gg text context vao=$vao')
@ -225,13 +217,13 @@ pub fn new_context(cfg gg.Cfg) *Context {
// gl.bind_buffer(GL_ARRAY_BUFFER, uint(0)) // gl.bind_buffer(GL_ARRAY_BUFFER, uint(0))
// # glBindVertexArray(0); // # glBindVertexArray(0);
mut ctx := &Context { mut ctx := &Context {
shader: shader, shader: shader
width: width, width: width
height: height, height: height
scale: scale scale: scale
vao: vao, vao: vao
vbo: vbo, vbo: vbo
chars: chars, chars: chars
face: f face: f
} }
ctx.init_utf8_runes() ctx.init_utf8_runes()
@ -239,10 +231,11 @@ pub fn new_context(cfg gg.Cfg) *Context {
} }
// A dirty hack to implement rendering of cyrillic letters. // A dirty hack to implement rendering of cyrillic letters.
// All UTF-8 must be supported. // All UTF-8 must be supported.
fn (ctx mut Context) init_utf8_runes() { fn (ctx mut Context) init_utf8_runes() {
s := 'йцукенгшщзхъфывапролджэячсмитьбюЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ' s := 'йцукенгшщзхъфывапролджэячсмитьбюЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ'
println(s) print('init utf8 runes: ')
//println(s)
us := s.ustring() us := s.ustring()
for i := 0; i < us.len; i++ { for i := 0; i < us.len; i++ {
_rune := us.at(i) _rune := us.at(i)
@ -253,25 +246,18 @@ fn (ctx mut Context) init_utf8_runes() {
} }
} }
// fn (ctx &GG) render_text(text string, x, y, scale f32, color gx.Color) { pub fn (ctx mut Context) draw_text(_x, _y int, text string, cfg gx.TextCfg) {
pub fn (ctx &Context) draw_text(_x, _y int, text string, cfg gx.TextCfg) {
//utext := text.ustring_tmp() //utext := text.ustring_tmp()
utext := text.ustring() utext := text.ustring()
ctx._draw_text(_x, _y, utext, cfg) ctx._draw_text(_x, _y, utext, cfg)
// utext.free()
// # glScissor(0,0,ctx->width*2,ctx->height*2);
// gl.disable(GL_SCISSOR_TEST)// TODO
// #free(text.str);
} }
fn (ctx &Context) draw_text_fast(_x, _y int, text ustring, cfg gx.TextCfg) { fn (ctx mut Context) draw_text_fast(_x, _y int, text ustring, cfg gx.TextCfg) {
ctx._draw_text(_x, _y, text, cfg) ctx._draw_text(_x, _y, text, cfg)
} }
// TODO HACK with second text context fn (ctx mut Context) _draw_text(_x, _y int, utext ustring, cfg gx.TextCfg) {
// fn (ctx &GG) _draw_text(_x, _y int, text string, cfg gx.TextCfg) { /*
fn (ctx &Context) _draw_text(_x, _y int, utext ustring, cfg gx.TextCfg) {
/*
if utext.s.contains('on_seg') { if utext.s.contains('on_seg') {
println('\nat(0)') println('\nat(0)')
println(utext.runes) println(utext.runes)
@ -282,8 +268,8 @@ fn (ctx &Context) _draw_text(_x, _y int, utext ustring, cfg gx.TextCfg) {
} }
} }
*/ */
mut x := f32(_x) mut x := f32(_x)
mut y := f32(_y) mut y := f32(_y)
// println('scale=$ctx.scale size=$cfg.size') // println('scale=$ctx.scale size=$cfg.size')
if cfg.align == gx.ALIGN_RIGHT { if cfg.align == gx.ALIGN_RIGHT {
width := utext.len * 7 width := utext.len * 7
@ -293,12 +279,12 @@ fn (ctx &Context) _draw_text(_x, _y int, utext ustring, cfg gx.TextCfg) {
// println('y=$_y height=$ctx.height') // println('y=$_y height=$ctx.height')
// _y = _y * int(ctx.scale) //+ 26 // _y = _y * int(ctx.scale) //+ 26
y = y * int(ctx.scale) + ((cfg.size * ctx.scale) / 2) + 5 * ctx.scale y = y * int(ctx.scale) + ((cfg.size * ctx.scale) / 2) + 5 * ctx.scale
y = f32(ctx.height) - y y = f32(ctx.height) - y
color := cfg.color color := cfg.color
// Activate corresponding render state // Activate corresponding render state
ctx.shader.use() ctx.shader.use()
ctx.shader.set_color('textColor', color) ctx.shader.set_color('textColor', color)
# glActiveTexture(GL_TEXTURE0); C.glActiveTexture(C.GL_TEXTURE0)
gl.bind_vao(ctx.vao) gl.bind_vao(ctx.vao)
// Iterate through all characters // Iterate through all characters
// utext := text.ustring() // utext := text.ustring()
@ -318,65 +304,52 @@ fn (ctx &Context) _draw_text(_x, _y int, utext ustring, cfg gx.TextCfg) {
// TODO O(1) use map // TODO O(1) use map
for j := 0; j < ctx.utf_runes.len; j++ { for j := 0; j < ctx.utf_runes.len; j++ {
rune_j := ctx.utf_runes[j] rune_j := ctx.utf_runes[j]
// if string_eq(ctx.utf_runes[j], rune) {
if rune_j==_rune { if rune_j==_rune {
ch = ctx.utf_chars[j] ch = ctx.utf_chars[j]
break break
} }
} }
} }
if ch.size.x == 0 { if ch.size.x == 0 && _rune.len > 1{
c := _rune[0]
println('cant draw rune "$_rune" code=$c, loading')
continue
ch = ft_load_char(ctx.face, _rune.utf32_code())
println('done loading')
ctx.utf_runes << _rune
ctx.utf_chars << ch
// continue // continue
} }
// mut c := int(text[i])
// c = 128
// s := 'A'
// c := int(s[0])
// ch := ctx.chars[c]
xpos := x + f32(ch.bearing.x) * 1 xpos := x + f32(ch.bearing.x) * 1
ypos := y - f32(ch.size.y - ch.bearing.y) * 1 ypos := y - f32(ch.size.y - ch.bearing.y) * 1
w := f32(ch.size.x) * 1 w := f32(ch.size.x) * 1
h := f32(ch.size.y) * 1 h := f32(ch.size.y) * 1
// Update VBO for each character // Update VBO for each character
# GLfloat vertices[6][4] = { vertices := [
# { xpos, ypos + h, 0.0, 0.0 }, xpos, ypos + h, 0.0, 0.0 ,
# { xpos, ypos, 0.0, 1.0 }, xpos, ypos, 0.0, 1.0 ,
# { xpos + w, ypos, 1.0, 1.0 }, xpos + w, ypos, 1.0, 1.0 ,
# { xpos, ypos + h, 0.0, 0.0 }, xpos, ypos + h, 0.0, 0.0 ,
# { xpos + w, ypos, 1.0, 1.0 }, xpos + w, ypos, 1.0, 1.0 ,
# { xpos + w, ypos + h, 1.0, 0.0 } xpos + w, ypos + h, 1.0, 0.0
# }; ]
// t := glfw.get_time()
// Render glyph texture over quad // Render glyph texture over quad
// t1 := glfw.get_time() C.glBindTexture(C.GL_TEXTURE_2D, ch.texture_id)
# glBindTexture(GL_TEXTURE_2D, ch.texture_id);
// Update content of VBO memory // Update content of VBO memory
gl.bind_buffer(GL_ARRAY_BUFFER, ctx.vbo) gl.bind_buffer(GL_ARRAY_BUFFER, ctx.vbo)
// t2 := glfw.get_time() // glBufferSubData(..)
// # glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferData C.glBufferData(GL_ARRAY_BUFFER, 96, vertices.data, C.GL_DYNAMIC_DRAW)
# glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
// t3 := glfw.get_time()
// gl.bind_buffer(GL_ARRAY_BUFFER, uint(0))
// t4 := glfw.get_time()
// Render quad // Render quad
gl.draw_arrays(GL_TRIANGLES, 0, 6) gl.draw_arrays(GL_TRIANGLES, 0, 6)
// t5 := glfw.get_time()
// # if (glfw__get_time() - t > 0.001)
// {
// # printf("do_text = %f '%s' \n", glfw__get_time() - t, text.str);
// # printf("t1=%f, t2=%f, t3=%f, t4=%f, t5=%f\n\n\n", t1-t, t2-t1, t3-t2, t4-t3, t5-t4);
// }
// Now advance cursors for next glyph (note that advance is number of 1/64 pixels) // Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
// Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels)) // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))
# x += (ch.advance >> 6) * 1; x += ch.advance >> u32(6)
} }
gl.bind_vao(u32(0)) gl.bind_vao(u32(0))
# glBindTexture(GL_TEXTURE_2D, 0); C.glBindTexture(C.GL_TEXTURE_2D, 0)
// runes.free()
// #free(runes.data);
} }
pub fn (ctx &Context) draw_text_def(x, y int, text string) { pub fn (ctx mut Context) draw_text_def(x, y int, text string) {
cfg := gx.TextCfg { cfg := gx.TextCfg {
color: gx.Black, color: gx.Black,
size: DEFAULT_FONT_SIZE, size: DEFAULT_FONT_SIZE,

View File

@ -19,7 +19,7 @@ struct Context {
last_name ui.TextBox last_name ui.TextBox
age ui.TextBox age ui.TextBox
users []User users []User
window *ui.Window window &ui.Window
txt_pos int txt_pos int
} }