commit 29249800356b138ba586dd6e62005f38848a2edc Author: Sander Schobers Date: Tue Oct 3 20:38:09 2017 +0200 Basic wrapper around allegro5 libraries - Depends on optional allegro libraries font, image, primitives and ttf as well. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c8f6689 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vscode +debug +debug.test \ No newline at end of file diff --git a/allegro5/bitmap.go b/allegro5/bitmap.go new file mode 100644 index 0000000..9f0f494 --- /dev/null +++ b/allegro5/bitmap.go @@ -0,0 +1,97 @@ +package allegro5 + +// #include +// #include +import "C" + +import ( + "fmt" + "unsafe" +) + +// Bitmap represents an in memory bitmap +type Bitmap struct { + bitmap *C.ALLEGRO_BITMAP + width int + height int +} + +type DrawOptions struct { + Center bool + Scale *Scale + Tint *Color +} + +type Scale struct { + Horizontal float32 + Vertical float32 +} + +// NewBitmap creates a new bitmap of given width and height +func NewBitmap(width, height int) (*Bitmap, error) { + b := C.al_create_bitmap(C.int(width), C.int(height)) + if nil == b { + return nil, fmt.Errorf("Error creating bitmap") + } + return &Bitmap{b, width, height}, nil +} + +// LoadBitmap tries to load the image at the specified path as a bitmap +func LoadBitmap(path string) (*Bitmap, error) { + p := C.CString(path) + defer C.free(unsafe.Pointer(p)) + b := C.al_load_bitmap(p) + if nil == b { + return nil, fmt.Errorf("Error loading bitmap") + } + width := int(C.al_get_bitmap_width(b)) + height := int(C.al_get_bitmap_height(b)) + return &Bitmap{b, width, height}, nil +} + +// Draw draws the bitmap at the given location +func (b *Bitmap) Draw(left, top float32) { + C.al_draw_bitmap(b.bitmap, C.float(left), C.float(top), 0) +} + +func (b *Bitmap) DrawOptions(left, top float32, options DrawOptions) { + width := float32(b.width) + height := float32(b.height) + + scale := nil != options.Scale + if scale { + width *= options.Scale.Horizontal + height *= options.Scale.Vertical + } + if options.Center { + left -= width * 0.5 + top -= height * 0.5 + } + + if scale { + if nil == options.Tint { + C.al_draw_scaled_bitmap(b.bitmap, 0, 0, C.float(b.width), C.float(b.height), C.float(left), C.float(top), C.float(width), C.float(height), 0) + } else { + C.al_draw_tinted_scaled_bitmap(b.bitmap, options.Tint.color, 0, 0, C.float(b.width), C.float(b.height), C.float(left), C.float(top), C.float(width), C.float(height), 0) + } + } else { + if nil == options.Tint { + C.al_draw_bitmap(b.bitmap, C.float(left), C.float(top), 0) + } else { + C.al_draw_tinted_bitmap(b.bitmap, options.Tint.color, C.float(left), C.float(top), 0) + } + } +} + +func (b *Bitmap) Width() int { + return b.width +} + +func (b *Bitmap) Height() int { + return b.height +} + +// Destroy destroys the bitmap +func (b *Bitmap) Destroy() { + C.al_destroy_bitmap(b.bitmap) +} \ No newline at end of file diff --git a/allegro5/c.go b/allegro5/c.go new file mode 100644 index 0000000..0d2e571 --- /dev/null +++ b/allegro5/c.go @@ -0,0 +1,9 @@ +package allegro5 + +// #cgo linux pkg-config: allegro-5 allegro_font-5 allegro_image-5 allegro_primitives-5 allegro_ttf-5 +// #cgo windows LDFLAGS: -lallegro +// #cgo windows LDFLAGS: -lallegro_font +// #cgo windows LDFLAGS: -lallegro_image +// #cgo windows LDFLAGS: -lallegro_primitives +// #cgo windows LDFLAGS: -lallegro_ttf +import "C" diff --git a/allegro5/color.go b/allegro5/color.go new file mode 100644 index 0000000..1afa266 --- /dev/null +++ b/allegro5/color.go @@ -0,0 +1,16 @@ +package allegro5 + +// #include +import "C" + +type Color struct { + color C.ALLEGRO_COLOR +} + +func NewColor(r, g, b byte) Color { + return Color{C.al_map_rgb(C.uchar(r), C.uchar(g), C.uchar(b))} +} + +func NewColorAlpha(r, g, b, a byte) Color { + return Color{C.al_map_rgba(C.uchar(r), C.uchar(g), C.uchar(b), C.uchar(a))} +} \ No newline at end of file diff --git a/allegro5/display.go b/allegro5/display.go new file mode 100644 index 0000000..3f73ff2 --- /dev/null +++ b/allegro5/display.go @@ -0,0 +1,73 @@ +package allegro5 + +// #include +import "C" + +import ( + "fmt" + "unsafe" +) + +// Display represents a display +type Display struct { + display *C.ALLEGRO_DISPLAY +} + +type NewDisplayOptions struct { + Width int + Height int + Fullscreen bool + Resizable bool + Windowed bool +} + +// NewDisplay creates a display +func NewDisplay(options NewDisplayOptions) (*Display, error) { + var flags C.int = C.ALLEGRO_WINDOWED + if options.Fullscreen { + if options.Windowed { + flags |= C.ALLEGRO_FULLSCREEN_WINDOW + } else { + flags = C.ALLEGRO_FULLSCREEN + } + } + if options.Resizable { + flags |= C.ALLEGRO_RESIZABLE + } + C.al_set_new_display_flags(flags) + d := C.al_create_display(C.int(options.Width), C.int(options.Height)) + if nil == d { + return nil, fmt.Errorf("Error creating display") + } + return &Display{d}, nil +} + +// Flips flips the buffer to the display +func (d *Display) Flip() { + C.al_flip_display() +} + +func (d *Display) Width() int { + return int(C.al_get_display_width(d.display)) +} + +func (d *Display) Height() int { + return int(C.al_get_display_height(d.display)) +} + +func (d *Display) SetWindowTitle(title string) { + t := C.CString(title) + defer C.free(unsafe.Pointer(t)) + C.al_set_window_title(d.display, t) +} + +// Destroy destroys the display +func (d *Display) Destroy() { + C.al_destroy_display(d.display) +} + +func SetNewWindowTitle(title string) { + t := C.CString(title) + defer C.free(unsafe.Pointer(t)) + C.al_set_new_window_title(t) +} diff --git a/allegro5/event.go b/allegro5/event.go new file mode 100644 index 0000000..577f9c6 --- /dev/null +++ b/allegro5/event.go @@ -0,0 +1,140 @@ +package allegro5 + +// #include +import "C" + +import ( + "fmt" + "unsafe" +) + +type EventQueue struct { + queue *C.ALLEGRO_EVENT_QUEUE +} + +type Event interface { + Stamp() float64 +} + +type EventBase struct { + stamp float64 +} + +func (eb EventBase) Stamp() float64 { + return eb.stamp +} + +type DisplayCloseEvent struct { + EventBase +} + +type KeyEvent struct { + EventBase + KeyCode int + Display *Display +} + +type KeyCharEvent struct { + KeyEvent + UnicodeCharacter rune + Modifiers uint + Repeat bool +} + +type KeyDownEvent struct { + KeyEvent +} + +type KeyUpEvent struct { + KeyEvent +} + +type MouseButtonDownEvent struct { + MouseEvent + Button uint + Pressure float32 +} + +type MouseButtonUpEvent struct { + MouseEvent + Button uint + Pressure float32 +} + +type MouseEvent struct { + EventBase + X, Y int + Z, W int + Display *Display +} + +type MouseMoveEvent struct { + MouseEvent + DeltaX, DeltaY int + DeltaZ, DeltaW int + Pressure float32 +} + +func NewEventQueue() (*EventQueue, error) { + q := C.al_create_event_queue() + if nil == q { + return nil, fmt.Errorf("Unable to create event queue") + } + return &EventQueue{q}, nil +} + +func (eq *EventQueue) register(source *C.ALLEGRO_EVENT_SOURCE) { + C.al_register_event_source(eq.queue, source) +} + +func (eq *EventQueue) RegisterDisplay(d *Display) { + eq.register(C.al_get_display_event_source(d.display)) +} + +func (eq *EventQueue) RegisterMouse() { + eq.register(C.al_get_mouse_event_source()) +} + +func (eq *EventQueue) RegisterKeyboard() { + eq.register(C.al_get_keyboard_event_source()) +} + +func (eq *EventQueue) mapEvent(e *C.ALLEGRO_EVENT) Event { + any := (*C.ALLEGRO_ANY_EVENT)(unsafe.Pointer(e)) + eb := EventBase{float64(any.timestamp)} + switch any._type { + case C.ALLEGRO_EVENT_DISPLAY_CLOSE: + return &DisplayCloseEvent{EventBase{float64(any.timestamp)}} + case C.ALLEGRO_EVENT_MOUSE_AXES: + mouse := (*C.ALLEGRO_MOUSE_EVENT)(unsafe.Pointer(e)) + return &MouseMoveEvent{MouseEvent{eb, int(mouse.x), int(mouse.y), int(mouse.z), int(mouse.w), nil}, int(mouse.dx), int(mouse.dy), int(mouse.dz), int(mouse.dw), float32(mouse.pressure)} + case C.ALLEGRO_EVENT_MOUSE_BUTTON_DOWN: + mouse := (*C.ALLEGRO_MOUSE_EVENT)(unsafe.Pointer(e)) + return &MouseButtonDownEvent{MouseEvent{eb, int(mouse.x), int(mouse.y), int(mouse.z), int(mouse.w), nil}, uint(mouse.button), float32(mouse.pressure)} + case C.ALLEGRO_EVENT_MOUSE_BUTTON_UP: + mouse := (*C.ALLEGRO_MOUSE_EVENT)(unsafe.Pointer(e)) + return &MouseButtonUpEvent{MouseEvent{eb, int(mouse.x), int(mouse.y), int(mouse.z), int(mouse.w), nil}, uint(mouse.button), float32(mouse.pressure)} + case C.ALLEGRO_EVENT_KEY_DOWN: + key := (*C.ALLEGRO_KEYBOARD_EVENT)(unsafe.Pointer(e)) + return &KeyDownEvent{KeyEvent{eb, int(key.keycode), nil}} + case C.ALLEGRO_EVENT_KEY_UP: + key := (*C.ALLEGRO_KEYBOARD_EVENT)(unsafe.Pointer(e)) + return &KeyUpEvent{KeyEvent{eb, int(key.keycode), nil}} + case C.ALLEGRO_EVENT_KEY_CHAR: + key := (*C.ALLEGRO_KEYBOARD_EVENT)(unsafe.Pointer(e)) + return &KeyCharEvent{KeyEvent{eb, int(key.keycode), nil}, rune(key.unichar), uint(key.modifiers), bool(key.repeat)} + } + return nil +} + +func (eq *EventQueue) Get() Event { + var event C.ALLEGRO_EVENT + if !bool(C.al_get_next_event(eq.queue, &event)) { + return nil + } + return eq.mapEvent(&event) +} + +func (eq *EventQueue) Destroy() { + C.al_destroy_event_queue(eq.queue) +} \ No newline at end of file diff --git a/allegro5/font.go b/allegro5/font.go new file mode 100644 index 0000000..d63460c --- /dev/null +++ b/allegro5/font.go @@ -0,0 +1,58 @@ +package allegro5 + +// #include +// #include +// #include +import "C" + +import ( + "fmt" + "unsafe" +) + +type Font struct { + font *C.ALLEGRO_FONT +} + +type HorizontalAlignment int + +const ( + AlignLeft HorizontalAlignment = iota + AlignCenter + AlignRight +) + +func LoadTTFFont(path string, size int) (*Font, error) { + p := C.CString(path) + defer C.free(unsafe.Pointer(p)) + + f := C.al_load_ttf_font(p, C.int(size), 0) + if nil == f { + return nil, fmt.Errorf("Unable to load TTF font '%s'", path) + } + return &Font{f}, nil +} + +func (f *Font) drawFlags(a HorizontalAlignment) C.int { + switch a { + case AlignLeft: + return C.ALLEGRO_ALIGN_LEFT + case AlignCenter: + return C.ALLEGRO_ALIGN_CENTRE + case AlignRight: + return C.ALLEGRO_ALIGN_RIGHT + } + return C.ALLEGRO_ALIGN_LEFT +} + +func (f *Font) Draw(left, top float32, color Color, align HorizontalAlignment, text string) { + t := C.CString(text) + defer C.free(unsafe.Pointer(t)) + + flags := f.drawFlags(align) + C.al_draw_text(f.font, color.color, C.float(left), C.float(top), flags, t) +} + +func (f *Font) Destroy() { + C.al_destroy_font(f.font) +} \ No newline at end of file diff --git a/allegro5/graphics.go b/allegro5/graphics.go new file mode 100644 index 0000000..1411b2c --- /dev/null +++ b/allegro5/graphics.go @@ -0,0 +1,26 @@ +package allegro5 + +// #include +import "C" + +// BitmapFlag is extra information provided for creating a bitmap +type BitmapFlag int + +const ( + // BitmapFlagLinearScaleDown enables linear scaling when scaling down. Gives better results when combined with BitmapFlagMipMap + BitmapFlagLinearScaleDown BitmapFlag = C.ALLEGRO_MIN_LINEAR + // BitmapFlagLinearScaleUp enables linear scaling when scaling up. + BitmapFlagLinearScaleUp = C.ALLEGRO_MAG_LINEAR + // BitmapFlagMipMap enables mipmaps for drawing a scaled down version. Bitmap must square and its sides must be a power of two. + BitmapFlagMipMap = C.ALLEGRO_MIPMAP +) + +// ClearToColor clears the target bitmap to the color +func ClearToColor(c Color) { + C.al_clear_to_color(c.color) +} + +// SetNewBitmapFlags sets the default bitmap flags for a newly created bitmap +func SetNewBitmapFlags(flags BitmapFlag) { + C.al_set_new_bitmap_flags(C.int(flags)) +} \ No newline at end of file diff --git a/allegro5/primitives.go b/allegro5/primitives.go new file mode 100644 index 0000000..5c46f2b --- /dev/null +++ b/allegro5/primitives.go @@ -0,0 +1,9 @@ +package allegro5 + +// #include +// #include +import "C" + +func DrawLine(x1, y1, x2, y2 float32, c Color, thickness float32) { + C.al_draw_line(C.float(x1), C.float(y1), C.float(x2), C.float(y2), c.color, C.float(thickness)) +} diff --git a/allegro5/system.go b/allegro5/system.go new file mode 100644 index 0000000..1533604 --- /dev/null +++ b/allegro5/system.go @@ -0,0 +1,49 @@ +package allegro5 + +// #include +// #include +// #include +// #include +// #include +// bool init() { +// return al_init(); +// } +import "C" + +import ( + "fmt" +) + +type InitConfig struct { + Font bool + Image bool + Primitives bool + Keyboard bool + Mouse bool +} + +// Init initializes the Allegro system +func Init(config InitConfig) error { + if !bool(C.init()) { + return fmt.Errorf("Failed to initialize Allegro") + } + if config.Font && !bool(C.al_init_font_addon()) { + return fmt.Errorf("Failed to initialize font addon") + } + if config.Font && !bool(C.al_init_ttf_addon()) { + return fmt.Errorf("Failed to initialize ttf addon") + } + if config.Image && !bool(C.al_init_image_addon()) { + return fmt.Errorf("Failed to initialize image addon") + } + if config.Primitives && !bool(C.al_init_primitives_addon()) { + return fmt.Errorf("Failed to initialize primitives addon") + } + if config.Keyboard && !bool(C.al_install_keyboard()) { + return fmt.Errorf("Failed to install keyboard") + } + if config.Mouse && !bool(C.al_install_mouse()) { + return fmt.Errorf("Failed to install mouse") + } + return nil +}