Go调用SDL2的C语言动态库

栏目: C · 发布时间: 5年前

内容简介:在网上找到go的GUI项目基本都需要安装额外软件库,尝试了一下使用go调用dll库的,有所收获,抛砖引玉,留个记录怕以后忘了。

Go调用SDL2的 C语言 动态库

在网上找到 go 的GUI项目基本都需要安装额外软件库,尝试了一下使用go调用dll库的,有所收获,抛砖引玉,留个记录怕以后忘了。

1.目录结构

└─libtest
    │  main.go
    │  sdl.go
    └─ lib
            SDL2.dll

2. 库函数封装(sdl.go)

package main

import (
	"syscall"
	"unsafe"
)

//SDL_INIT_Flag 初始化标志
const (
	SDL_INIT_TIMER          = 0x00000001
	SDL_INIT_AUDIO          = 0x00000010
	SDL_INIT_VIDEO          = 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
	SDL_INIT_JOYSTICK       = 0x00000200 /**< SDL_INIT_JOYSTICK implies SDL_INIT_EVENTS */
	SDL_INIT_HAPTIC         = 0x00001000
	SDL_INIT_GAMECONTROLLER = 0x00002000 /**< SDL_INIT_GAMECONTROLLER implies SDL_INIT_JOYSTICK */
	SDL_INIT_EVENTS         = 0x00004000
	SDL_INIT_NOPARACHUTE    = 0x00100000 /**< compatibility; this flag is ignored. */
	SDL_INIT_EVERYTHING     = SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS |
		SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER
)

// SDL_WindowFlags 窗体标志
const (
	/* !!! FIXME: change this to name = (1<<x). */
	SDL_WINDOW_FULLSCREEN         = 0x00000001 /**< fullscreen window */
	SDL_WINDOW_OPENGL             = 0x00000002 /**< window usable with OpenGL context */
	SDL_WINDOW_SHOWN              = 0x00000004 /**< window is visible */
	SDL_WINDOW_HIDDEN             = 0x00000008 /**< window is not visible */
	SDL_WINDOW_BORDERLESS         = 0x00000010 /**< no window decoration */
	SDL_WINDOW_RESIZABLE          = 0x00000020 /**< window can be resized */
	SDL_WINDOW_MINIMIZED          = 0x00000040 /**< window is minimized */
	SDL_WINDOW_MAXIMIZED          = 0x00000080 /**< window is maximized */
	SDL_WINDOW_INPUT_GRABBED      = 0x00000100 /**< window has grabbed input focus */
	SDL_WINDOW_INPUT_FOCUS        = 0x00000200 /**< window has input focus */
	SDL_WINDOW_MOUSE_FOCUS        = 0x00000400 /**< window has mouse focus */
	SDL_WINDOW_FULLSCREEN_DESKTOP = (SDL_WINDOW_FULLSCREEN | 0x00001000)
	SDL_WINDOW_FOREIGN            = 0x00000800 /**< window not created by SDL */
	SDL_WINDOW_ALLOW_HIGHDPI      = 0x00002000 /**< window should be created in high-DPI mode if supported.
	  On macOS NSHighResolutionCapable must be set true in the
	  application's Info.plist for this to have any effect. */
	SDL_WINDOW_MOUSE_CAPTURE = 0x00004000 /**< window has mouse captured (unrelated to INPUT_GRABBED) */
	SDL_WINDOW_ALWAYS_ON_TOP = 0x00008000 /**< window should always be above others */
	SDL_WINDOW_SKIP_TASKBAR  = 0x00010000 /**< window should not be added to the taskbar */
	SDL_WINDOW_UTILITY       = 0x00020000 /**< window should be treated as a utility window */
	SDL_WINDOW_TOOLTIP       = 0x00040000 /**< window should be treated as a tooltip */
	SDL_WINDOW_POPUP_MENU    = 0x00080000 /**< window should be treated as a popup menu */
	SDL_WINDOW_VULKAN        = 0x10000000 /**< window usable for Vulkan surface */
)

// SDL_Window SDL窗体
type SDL_Window struct{}

// SDL_Event SDL 事件
type SDL_Event struct{}

// SDL_Init SDL 初始化
func SDL_Init(flags uint32) bool {
	ret := SysCallDll("SDL_Init", 1, uintptr(flags))
	if int(ret) == 0 {
		return true
	}
	return false
}

// SDL_CreateWindow 创建window窗体,失败返回nil
func SDL_CreateWindow(title string, posX, posY, width, height, sdlWindowFlag int32) *SDL_Window {
	var tp, _ = syscall.BytePtrFromString(title) // 将string转为*byte
	var tptr = unsafe.Pointer(tp)                // 获取*byte指针
	ret := SysCallDll("SDL_CreateWindow", 6, uintptr(tptr), uintptr(posX), uintptr(posY),
		uintptr(width), uintptr(height), uintptr(sdlWindowFlag))
	if int(ret) == 0 {
		return nil
	}
	return (*SDL_Window)(unsafe.Pointer(ret))
}

// SDL_Delay 延迟xxx ms后返回
func SDL_Delay(ms uint32) {
	SysCallDll("SDL_Delay", 1, uintptr(ms))
}

// SDL_DestroyWindow 销毁窗体
func SDL_DestroyWindow(window *SDL_Window) {
	SysCallDll("SDL_DestroyWindow", 1, uintptr(unsafe.Pointer(window)))
}

//SDL_Quit(void) 退出SDL系统
func SDL_Quit() {
	SysCallDll("SDL_Quit", 0)
}

// SDL_PollEvent  轮询当前挂起的事件
func SDL_PollEvent(event *SDL_Event) bool {
	ret := SysCallDll("SDL_PollEvent", 1, uintptr(unsafe.Pointer(event)))
	if int(ret) == 1 {
		return true
	}
	return false
}

3. 加载动态库(main.go)

package main

import (
	"fmt"

	"syscall"
	"unsafe"
)

// ThrowErr 抛出异常
func ThrowErr(str string, err error) {
	if err != nil {
		fmt.Printf("[Err]:%s,%v\n", str, err)
	}
}

// 动态库的指针
var hsdl syscall.Handle

// LoadDLL 加载动态库
func LoadDll() {
	var err error
	hsdl, err = syscall.LoadLibrary("lib/SDL2.dll")
	ThrowErr("LoadLibrary-SDL", err)
}

// FreeDll 释放动态库
func FreeDll() {
	if hsdl != 0 {
		syscall.FreeLibrary(hsdl)
	}

}

// SysCallDll  包装syscall.Syscall/6/9/12 调用动态库内函数
// funcName 函数名; argsNum 参数个数; args 可变参数
func SysCallDll(funcName string, argsNum int32, args ...uintptr) (r uintptr) {
	proc, err := syscall.GetProcAddress(hsdl, funcName)
	ThrowErr(funcName, err)
	var errs error
	var debug bool = false
	var fargs = [12]uintptr{} //获取参数
	for i, arg := range args {
		fargs[i] = arg
	}
	if (argsNum > -1) && (argsNum < 4) { //小于等于3个参数
		r, _, errs = syscall.Syscall(uintptr(proc), uintptr(argsNum),
			fargs[0], fargs[1], fargs[2])
		//ThrowErr(funcName, errs)
	} else if (argsNum > 3) && (argsNum < 7) { //小于等于6个参数
		r, _, errs = syscall.Syscall6(uintptr(proc), uintptr(argsNum),
			fargs[0], fargs[1], fargs[2], fargs[3], fargs[4], fargs[5])
		//ThrowErr(funcName, errs)
	} else if (argsNum > 6) && (argsNum < 10) { //小于等于9个参数
		r, _, errs = syscall.Syscall9(uintptr(proc), uintptr(argsNum), fargs[0], fargs[1],
			fargs[2], fargs[3], fargs[4], fargs[5], fargs[6], fargs[7], fargs[8])
		//ThrowErr(funcName, errs)
	} else if (argsNum > 9) && (argsNum < 13) { //小于等于12个参数
		r, _, errs = syscall.Syscall12(uintptr(proc), uintptr(argsNum), fargs[0], fargs[1],
			fargs[2], fargs[3], fargs[4], fargs[5], fargs[6], fargs[7], fargs[8],
			fargs[9], fargs[10], fargs[11])
		//ThrowErr(funcName, errs)
	} else {
		fmt.Println("The args number was bigger than 12.")
	}
	if debug {
		fmt.Println(errs)
	}
	return r
}

// CharPtr2String 从char指针读取string
func CharPtr2String(vcode uintptr) string {
	var vbyte []byte
	for i := 0; i < 20; i++ {
		sbyte := *((*byte)(unsafe.Pointer(vcode)))
		if sbyte == 0 {
			break
		}
		vbyte = append(vbyte, sbyte)
		vcode += 1
	}
	return string(vbyte)
}

// testSdlWindow 创建窗体,然后暂停程序所以窗体没响应
func testSdlWindow() {
	LoadDll()                     // 加载动态库
	if SDL_Init(SDL_INIT_VIDEO) { // 初始化视频子系统
		// 创建窗体
		win := SDL_CreateWindow("SDL窗体标题", 50, 50, 600, 480, SDL_WINDOW_SHOWN)
		if win == nil {
			fmt.Println("SDL_CreateWindow 失败")
		}
		SDL_Delay(6000)        // 暂停程序6000ms
		SDL_DestroyWindow(win) // 销毁释放窗体
	}
	SDL_Quit()      // 退出清理SDL
	defer FreeDll() // 释放动态库SDL2.dll
}

// testSdlWindow2 创建窗体,使用事件循环显示可响应窗体
func testSdlWindow2() {
	LoadDll()                     // 加载动态库
	if SDL_Init(SDL_INIT_VIDEO) { // 初始化视频子系统
		// 创建窗体
		win := SDL_CreateWindow("SDL窗体标题", 50, 50, 600, 480, SDL_WINDOW_SHOWN)
		if win == nil {
			fmt.Println("SDL_CreateWindow 失败")
		}
		for { // 事件循环
			if SDL_PollEvent(evt) { // 轮询事件队列
				// 此处SDL_Event在C语言中为联合,需要其他处理
				// if GetEventType(evt) == SDL_QUIT { // 退出事件
				// 	break
				// }
			}
		}
		SDL_DestroyWindow(win) // 销毁释放窗体
		SDL_Quit()             // 退出清理SDL
		defer FreeDll()        // 释放动态库SDL2.dll
	}
}

var evt = new(SDL_Event) // SDL事件
func main() {
	testSdlWindow2()
}

Go调用SDL2的C语言动态库


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

程序员修炼之道

程序员修炼之道

[美]享特 / 人民邮电出版社 / 2007-12 / 49.00元

《程序员修炼之道》适合各层次软件开发人员阅读,也适合高等院校计算机专业学生和教师阅读。一起来看看 《程序员修炼之道》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具