Установка и работа с BLT на Linux
==

Про Windows и Mac в контексте связки Go + BLT, я говорить не буду, поскольку не ел устриц.
С Linux, которая моя основная рабочая ось - другая история, здесь вносят свой шарм особенности работы линкера.
Дело в том, что BLT написана на C. Но! есть готовые биндинги для Go, НО! В Terminal/Include/Go по умолчанию
указаны такие флаги линкера CGO (стр 25)
```go
// #cgo LDFLAGS: -lBearLibTerminal
```
Что подразумевает глобальную вивдимость библиотеки. Увы, пока пакета с BLT для распространенных дистрибутивов Linux нет.
Поэтому беде нужно помочь руками. Сначала вручную показать линтеру, что такая библиотека есть, и потом перезагрузить 
кеш путей к библиотекам::
```bash
$ sudo echo "/path/to/libbearterminal.so" > /etc/ld.so.conf.d/libbearterminal.conf && sudo ldconfig
```

Проблема тут в том, что эту же операцию придется проделать всем, кто захочет запустить ваше приложение с BLT. Вопреки 
распространенному стереотипу - доля красноглазых пользователей Linux с каждым годом падает, и эта консольная магия для 
большинства уже некомильфо.

Способ второй, которым воспользовался я, намного проще для пользователя.
Редактируем файл с биндингами примерно следующим образом:
```go
// #cgo LDFLAGS: -L. -Wl,-rpath -Wl,./ -lBearLibTerminal
// #include <stdlib.h>
// #include <BearLibTerminal.h>
import "C"
```
(знатоки С, простите, я этими флагами вообще 
пользоваться не умею)

Далее - собираем приложение с libtcod ```go build -o test```.

Проверяем, что относительные пути записались в бинарник:
```bash
objdump -p test | grep RPATH
```
Результат должен быть таким:
```bash
RPATH                ./
```

Ура! Теперь кладем libBearLibTerminal.so прямо в папку с main.go и запускам go run (или скомпилированный бинарник) прям 
оттуда. Собранные таким образом бинарники будут искать библиотеку в той же папке, где находятся они сами.

Теперь при дистрибуции приложения можно просто положить .so файл библиотеки рядом, и все будет работать!

Вторая кочка, на которой мне пришлось столкнуться - 
то то, что вызов любой своей функции не из main thread BLT воспринимает крайне нервно, 
и сыпет фатальными ошибками. Справедливости ради точно так же ведет себя и большинство 
других библиотек связанных с рендером и вводом-выводом, тот же SDL например. Так что 
воспринимайте это как милую особенность использования CGO.

Лекарство тут ровно одно - вызывать сишные foreign functions из main thread. 
Для реализации этого требования мне показалась полезной следующая конструкция
```go
package main 

import "runtime"

...


// Рецепт чтобы убежать от [fatal] 'refresh' was not called from the main thread
// https://github.com/golang/go/wiki/LockOSThread
func init() {
	runtime.LockOSThread()
}

type GameState struct {
	Mainfunc     chan func()
}

// do запускает функцию f в контексте main thread.
func (g *GameState) Do(f func()) {
	done := make(chan struct{}, 1)
	g.Mainfunc <- func() {
		f()
		f = nil //zero pointer в замыкание
		done <- struct{}{}
	}
	<-done
}


var State = GameState{
	Mainfunc:     make(chan func()), //блокирующий канал(!)
}
// И где-то в Main Loop делаем примерно так:
func MainLoop(state GameState) {
	...
	//В этом select обработка ввода, рендер, пеерколючение состояний интерфейса итп
	for f := range state.Mainfunc {
		f()
	}}
...
```

State - это обычный Value Object, экземпляр типа GameState. Я его использую как 
контейнер для важных для игры данных - уровня, состояния рендера, разных тикеров, 
каналов для рендера и ввода-вывода итп. Так как он глобальный (или просто передается 
по аргументам), то именно в него встроен метод Do. Если нам скажем в пакете где 
описывается некий предмет надо нарисовать при его поднятии какой-то супер-эффект 
на экране - мы поступаем вот так:

```go
package item

import "main"
import blt "some.repo.ru/user/bearlibterminal"

var State main.GameState 

//функция скажем поднятия описание предмета...
func (item *Item) Pickup() {
	
    ....
    doSuperEffect(State)
}
//and there we go like this:
func doSuperEffect(State main.GameState) {
 		...
		State.Do(func() {
			renderSuperEffect()
//				...do stuff in main thread
			})
 		...
}
...
func renderSuperEffect() {
	blt.Layer(0)
	blt.Print("WAAAGH")
	...
}
```

Здесь renderSuperEffect - непосредственная реализация эффекта, doSuperEffect - 
запихивает в очередь на выполнения в main thread эту самую реализацию. Которая с успехом 
выполняется в main loop. В целом картина именно такая, но больше подробностей можно
найти по ссылкам в комментариях.