Add a README
This commit is contained in:
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,52 @@
|
||||
package main
|
||||
|
||||
//宏
|
||||
const (
|
||||
//最大包头长度
|
||||
max_head_length uint32 = 4096 //4K
|
||||
//最大消息长度
|
||||
max_message_length uint32 = 0x6400000 //64M
|
||||
//最大头像大小
|
||||
max_photo_size uint32 = 0x400000 //4M
|
||||
//服务器地址
|
||||
server_addr string = "localhost:3727"
|
||||
)
|
||||
|
||||
//消息头的通用字段名称
|
||||
const (
|
||||
keyname_operation string = "Operation" //操作名
|
||||
keyname_token string = "Token"
|
||||
)
|
||||
|
||||
//控制消息
|
||||
const (
|
||||
operation_panic string = "Panic"
|
||||
operation_trans_message string = "TransMessage"
|
||||
operation_send_message string = "SendMessage"
|
||||
operation_ping string = "Ping"
|
||||
operation_login string = "Login"
|
||||
operation_logout string = "Logout"
|
||||
operation_offline string = "Offline"
|
||||
operation_get_user_list string = "GetUserList"
|
||||
operation_update_userinfo string = "UpdateUserInfo"
|
||||
)
|
||||
|
||||
//帮助
|
||||
const help_message string = `
|
||||
欢迎使用StormChat!
|
||||
输入“help”查看命令列表
|
||||
输入“.”退出命令模式,退出后可输入“.”重新进入命令模式
|
||||
聊天时可在行末输入‘\’以换行输入消息
|
||||
`
|
||||
|
||||
//指令列表
|
||||
const command_list string = `
|
||||
指令列表:
|
||||
. 退出命令模式 login 登录
|
||||
to 设定聊天对象 logout 注销
|
||||
friends 查看用户列表 help 显示命令列表
|
||||
scroll 消息面板翻页 clear 清空消息记录
|
||||
exit 退出程序
|
||||
CTRL + C 强制退出
|
||||
|
||||
`
|
||||
@@ -0,0 +1,256 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
#include <conio.h>
|
||||
#include <stdlib.h>
|
||||
int getkey() {
|
||||
return getch();
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
var commandMode bool //指示当前是否处于命令模式
|
||||
func main() {
|
||||
defer func() {
|
||||
fmt.Print("按任意键退出程序")
|
||||
C.getkey()
|
||||
DestroyConsoleWindow()
|
||||
}()
|
||||
InitConsole()
|
||||
client := NewSession()
|
||||
if client == nil {
|
||||
return
|
||||
}
|
||||
SetUserNameText("未登录")
|
||||
SetCurrentFriendText("")
|
||||
PrintInfo(help_message)
|
||||
if len(os.Args) >= 3 {
|
||||
client.Login(os.Args[1], os.Args[2])
|
||||
commandMode = false
|
||||
}
|
||||
commandMode = true
|
||||
PrintInfo(command_list)
|
||||
if InputCommand(client) {
|
||||
return
|
||||
}
|
||||
var message string
|
||||
var input string
|
||||
for {
|
||||
if message == "" {
|
||||
ClearInput()
|
||||
}
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
input, _ = reader.ReadString('\n')
|
||||
input = strings.TrimSuffix(input, "\r\n")
|
||||
input = strings.TrimSuffix(input, "\n")
|
||||
switch input {
|
||||
case ".":
|
||||
message = ""
|
||||
if InputCommand(client) {
|
||||
return
|
||||
}
|
||||
case ".quit":
|
||||
return
|
||||
case ".exit":
|
||||
return
|
||||
default:
|
||||
message += input
|
||||
if strings.HasSuffix(input, "\\") {
|
||||
message = strings.TrimSuffix(message, "\\")
|
||||
message += "\r\n"
|
||||
} else {
|
||||
client.SendMessage([]byte(message))
|
||||
message = ""
|
||||
} //发送成功,清空消息缓存
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//输入指令
|
||||
func InputCommand(client *Session) bool {
|
||||
defer SetOperationText("聊天")
|
||||
SetOperationText("命令模式")
|
||||
for {
|
||||
ClearInput()
|
||||
var input string
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
input, _ = reader.ReadString('\n')
|
||||
input = strings.TrimSuffix(input, "\r\n")
|
||||
input = strings.TrimSuffix(input, "\n")
|
||||
switch strings.TrimRight(input, " ") {
|
||||
case "scroll":
|
||||
ScrollMessage()
|
||||
case "login":
|
||||
success := LoginWithInput(client)
|
||||
if !success {
|
||||
client.destroy()
|
||||
client = NewSession()
|
||||
PrintError("登录失败,请重试。")
|
||||
} //如果连接已中断则重新创建session
|
||||
case "logout":
|
||||
client.Logout()
|
||||
case "to":
|
||||
SwitchReceiver(client)
|
||||
case "friends":
|
||||
ViewFriends(client)
|
||||
case "clear":
|
||||
ClearOutput()
|
||||
case "help":
|
||||
PrintInfo(command_list)
|
||||
case ".":
|
||||
return false
|
||||
case "exit":
|
||||
return true
|
||||
default:
|
||||
PrintError("无效命令")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//发起登录。如果返回值为false则session已失效(根据发送数据失败判断)
|
||||
func LoginWithInput(session *Session) bool {
|
||||
SetOperationText("登录")
|
||||
defer func() {
|
||||
if commandMode {
|
||||
SetOperationText("命令模式")
|
||||
} else {
|
||||
SetOperationText("聊天")
|
||||
}
|
||||
}()
|
||||
if session.status == session_status_running {
|
||||
PrintError("已经登录,请先注销。")
|
||||
return true //虽然登录失败但连接仍有效,因此返回true
|
||||
}
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
PrintInputTip("user: ")
|
||||
user, _ := reader.ReadString('\n')
|
||||
user = strings.TrimSuffix(user, "\r\n")
|
||||
user = strings.TrimSuffix(user, "\n")
|
||||
PrintInputTip("password: ")
|
||||
pwd, _ := reader.ReadString('\n')
|
||||
pwd = strings.TrimSuffix(pwd, "\r\n")
|
||||
pwd = strings.TrimSuffix(pwd, "\n")
|
||||
if !session.Login(user, pwd) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//更换聊天对象
|
||||
func SwitchReceiver(session *Session) {
|
||||
if session.status != session_status_running {
|
||||
PrintError("请先登录")
|
||||
return
|
||||
}
|
||||
SetOperationText("切换好友")
|
||||
PrintInputTip("好友ID或昵称(输入*启用广播模式): ")
|
||||
var found bool = false //好友是否存在
|
||||
var friend string
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
friend, _ = reader.ReadString('\n')
|
||||
friend = strings.TrimSuffix(friend, "\r\n")
|
||||
friend = strings.TrimSuffix(friend, "\n")
|
||||
if friend == "*" {
|
||||
SetCurrentFriendText("广播模式")
|
||||
session.receiver = friend
|
||||
} //广播消息
|
||||
//查询好友列表
|
||||
for _, item := range session.friends {
|
||||
if strings.ToLower(item.NickName) == strings.ToLower(friend) || item.User == friend {
|
||||
SetCurrentFriendText(item.NickName)
|
||||
session.receiver = item.User
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if commandMode {
|
||||
SetOperationText("命令模式")
|
||||
} else {
|
||||
SetOperationText("聊天")
|
||||
}
|
||||
if !found {
|
||||
PrintError("未找到名为【" + friend + "】的好友")
|
||||
}
|
||||
}
|
||||
|
||||
//查看好友列表
|
||||
func ViewFriends(session *Session) {
|
||||
list := session.friends
|
||||
if session.status != session_status_running {
|
||||
PrintError("请先登录")
|
||||
return
|
||||
}
|
||||
if len(list) == 0 {
|
||||
PrintInfo("好友列表为空")
|
||||
return
|
||||
}
|
||||
for _, item := range list {
|
||||
PrintInfo(item.User + " " + item.NickName)
|
||||
}
|
||||
}
|
||||
|
||||
//滚动消息面板
|
||||
func ScrollMessage() {
|
||||
if commandMode {
|
||||
defer SetOperationText("命令模式")
|
||||
} else {
|
||||
defer SetOperationText("聊天")
|
||||
}
|
||||
defer ClearInput()
|
||||
SetOperationText("翻页模式")
|
||||
PrintInputTip("上翻:P\n下翻:N\n退出翻页:ESC\n")
|
||||
for {
|
||||
key := C.getkey()
|
||||
switch key {
|
||||
case 'p':
|
||||
ScrollOutputArea(-1)
|
||||
case 'n':
|
||||
ScrollOutputArea(1)
|
||||
case 27:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//输出错误
|
||||
func PrintError(err string) {
|
||||
PrintOutputLine("[ERROR] "+err, FOREGROUND_RED)
|
||||
}
|
||||
|
||||
//输出信息
|
||||
func PrintInfo(text string) {
|
||||
PrintOutputLine(text, FOREGROUND_RED|FOREGROUND_GREEN)
|
||||
}
|
||||
func PrintLog(str string) {
|
||||
PrintOutputLine("[INFO] "+str, FOREGROUND_INTENSITY)
|
||||
}
|
||||
|
||||
//输出消息头
|
||||
func PrintMessageHead(head string, intensity bool) {
|
||||
if intensity {
|
||||
PrintOutputLine(head, FOREGROUND_INTENSITY)
|
||||
} else {
|
||||
PrintOutputLine(head, FOREGROUND_GREEN)
|
||||
}
|
||||
}
|
||||
|
||||
//输出消息体
|
||||
func PrintMessage(msg string) {
|
||||
PrintOutputLine(msg, FORGROUND_WHITE)
|
||||
}
|
||||
|
||||
//构造消息头
|
||||
func MakeHead(operation string) map[string]string {
|
||||
headInfo := make(map[string]string)
|
||||
headInfo[keyname_token] = string(time.Now().UnixNano())
|
||||
headInfo[keyname_operation] = operation
|
||||
return headInfo
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
//当前session的状态
|
||||
const (
|
||||
session_status_created = iota //已创建,但未登录
|
||||
session_status_running // 通道已建立,session正常运行
|
||||
session_status_stoped // 标识连接已断开
|
||||
session_status_destroyed
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
sender *UserInfo //消息发送者
|
||||
receiver string //消息接收者
|
||||
friends []UserInfo //好友列表
|
||||
conn *net.TCPConn //TCP连接
|
||||
status int32 //session运行状态
|
||||
stopChan chan bool //Session终止标识,传递数据即终止。仅由读消息循环用于终止写循环
|
||||
writeCh chan []byte //写消息通道
|
||||
writeResultCh chan bool //是否写成功
|
||||
}
|
||||
|
||||
//初始化
|
||||
func NewSession() *Session {
|
||||
tcpAddr, _ := net.ResolveTCPAddr("tcp", server_addr)
|
||||
conn, err := net.DialTCP("tcp", nil, tcpAddr)
|
||||
if err != nil {
|
||||
PrintError("连接服务器失败:" + err.Error())
|
||||
return nil
|
||||
}
|
||||
var session = new(Session)
|
||||
session.sender = nil
|
||||
session.receiver = ""
|
||||
session.status = session_status_created
|
||||
session.stopChan = make(chan bool)
|
||||
session.writeCh = make(chan []byte)
|
||||
session.writeResultCh = make(chan bool)
|
||||
session.conn = conn
|
||||
go session.SendLoop()
|
||||
go session.ReceiveLoop()
|
||||
return session
|
||||
}
|
||||
|
||||
//销毁聊天连接
|
||||
func (session *Session) destroy() {
|
||||
if session.status == session_status_destroyed {
|
||||
return
|
||||
}
|
||||
session.status = session_status_destroyed
|
||||
session.conn.Close()
|
||||
close(session.writeCh)
|
||||
close(session.writeResultCh)
|
||||
}
|
||||
|
||||
//消息写循环
|
||||
func (session *Session) SendLoop() {
|
||||
defer session.destroy()
|
||||
for {
|
||||
select {
|
||||
case data, ok := <-session.writeCh:
|
||||
if !ok {
|
||||
atomic.SwapInt32(&session.status, session_status_stoped)
|
||||
continue
|
||||
}
|
||||
writeLen, err := session.conn.Write(data)
|
||||
if err != nil || (writeLen != len(data)) {
|
||||
PrintError(err.Error())
|
||||
atomic.SwapInt32(&session.status, session_status_stoped)
|
||||
session.writeResultCh <- false
|
||||
} //写失败
|
||||
session.writeResultCh <- true
|
||||
case <-session.stopChan:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//消息读循环
|
||||
func (session *Session) ReceiveLoop() {
|
||||
defer func() {
|
||||
session.stopChan <- true
|
||||
}() //终止写循环
|
||||
var err error //错误
|
||||
for {
|
||||
if atomic.LoadInt32(&session.status) >= session_status_stoped {
|
||||
return
|
||||
} //检查session是否已终止
|
||||
//读取数据
|
||||
head := session.readDataWithLength()
|
||||
data := session.readDataWithLength()
|
||||
|
||||
if head == nil || data == nil {
|
||||
PrintError("获取数据失败")
|
||||
atomic.SwapInt32(&session.status, session_status_stoped)
|
||||
continue
|
||||
}
|
||||
//解析包头
|
||||
headInfo := make(map[string]string)
|
||||
err = json.Unmarshal(head, &headInfo)
|
||||
if err != nil {
|
||||
PrintError("解析包头失败")
|
||||
continue
|
||||
}
|
||||
switch headInfo[keyname_operation] {
|
||||
case operation_send_message:
|
||||
session.ResultHandler(headInfo)
|
||||
break
|
||||
case operation_update_userinfo:
|
||||
session.ResultHandler(headInfo)
|
||||
break
|
||||
case operation_offline:
|
||||
case operation_panic:
|
||||
session.PanicHandler(headInfo)
|
||||
break
|
||||
case operation_login:
|
||||
session.LoginDoneHandler(headInfo, data)
|
||||
break
|
||||
case operation_trans_message:
|
||||
session.MessageHandler(headInfo, data)
|
||||
break
|
||||
case operation_get_user_list:
|
||||
session.GetUserListHandler(headInfo, data)
|
||||
break
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//处理请求结果反馈消息
|
||||
func (session *Session) ResultHandler(headInfo map[string]string) {
|
||||
if headInfo["Error"] == "" {
|
||||
PrintLog(headInfo[keyname_operation] + " successfully.")
|
||||
} else {
|
||||
PrintError("Failed to " + headInfo[keyname_operation] + ": " + headInfo["Error"])
|
||||
}
|
||||
}
|
||||
|
||||
//服务器报错
|
||||
func (session *Session) PanicHandler(headInfo map[string]string) {
|
||||
PrintError("Server Error: " + headInfo["Error"])
|
||||
atomic.SwapInt32(&session.status, session_status_stoped)
|
||||
}
|
||||
|
||||
//登录反馈
|
||||
func (session *Session) LoginDoneHandler(headInfo map[string]string, data []byte) {
|
||||
session.ResultHandler(headInfo)
|
||||
if headInfo["Error"] == "" {
|
||||
user := new(UserInfo)
|
||||
err := json.Unmarshal(data, user)
|
||||
if err != nil {
|
||||
PrintError("解析用户信息失败")
|
||||
return
|
||||
} else {
|
||||
session.sender = user
|
||||
SetUserNameText(user.NickName)
|
||||
}
|
||||
atomic.SwapInt32(&session.status, session_status_running)
|
||||
session.GetUserList()
|
||||
}
|
||||
}
|
||||
|
||||
//获取用户列表
|
||||
func (session *Session) GetUserListHandler(headInfo map[string]string, data []byte) {
|
||||
session.ResultHandler(headInfo)
|
||||
if headInfo["Error"] != "" {
|
||||
return
|
||||
}
|
||||
err := json.Unmarshal(data, &session.friends)
|
||||
if err != nil {
|
||||
PrintError("解析用户信息失败: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
//输出消息
|
||||
func (session *Session) MessageHandler(headInfo map[string]string, message []byte) {
|
||||
if headInfo["Error"] != "" {
|
||||
PrintError("发送消息失败: " + headInfo["Error"])
|
||||
return
|
||||
}
|
||||
var user *UserInfo = nil
|
||||
for _, item := range session.friends {
|
||||
if item.User == headInfo["From"] {
|
||||
user = &item
|
||||
break
|
||||
}
|
||||
}
|
||||
when, _ := time.Parse(time.RFC3339, headInfo["When"])
|
||||
if user == nil {
|
||||
PrintMessageHead(headInfo["From"]+" ["+when.Format("2006-01-02 15:04:05")+"]", false)
|
||||
} else {
|
||||
PrintMessageHead(user.NickName+" ["+when.Format("2006-01-02 15:04:05")+"]", false)
|
||||
}
|
||||
PrintMessage(string(message))
|
||||
if session.receiver == "" && user != nil {
|
||||
session.receiver = user.User
|
||||
SetCurrentFriendText(user.NickName)
|
||||
} //如果无当前聊天对象则将聊天对象设置为消息发送者
|
||||
}
|
||||
|
||||
//发送数据
|
||||
func (session *Session) send(head []byte, data []byte) bool {
|
||||
if atomic.LoadInt32(&session.status) >= session_status_stoped {
|
||||
return false
|
||||
}
|
||||
var buf = make([]byte, 4) //长度标识
|
||||
var packet = make([]byte, 0, len(head)+len(data)+4+4) //headLen + head + msgLen + data
|
||||
//写包头
|
||||
//取包头长
|
||||
binary.BigEndian.PutUint32(buf[0:4], uint32(len(head)))
|
||||
packet = append(packet, buf...) //写包头长度
|
||||
if len(head) > 0 {
|
||||
packet = append(packet, head...) //写包头
|
||||
}
|
||||
//写数据
|
||||
//取数据长
|
||||
binary.BigEndian.PutUint32(buf[0:4], uint32(len(data)))
|
||||
packet = append(packet, buf...) //写数据长度
|
||||
if len(data) > 0 {
|
||||
packet = append(packet, data...) //写数据
|
||||
}
|
||||
|
||||
if len(packet) != cap(packet) {
|
||||
return false
|
||||
} //数据异常
|
||||
if atomic.LoadInt32(&session.status) >= session_status_stoped {
|
||||
return false
|
||||
}
|
||||
session.writeCh <- packet
|
||||
return <-session.writeResultCh
|
||||
}
|
||||
|
||||
//读取数据
|
||||
func (session *Session) readDataWithLength() []byte {
|
||||
var dataLen uint32 //数据长度
|
||||
var lenBuffer [4]byte //数据长度缓存区
|
||||
var data []byte //数据缓存区
|
||||
//获取数据长度
|
||||
readedLen, err := session.conn.Read(lenBuffer[0:4])
|
||||
if err != nil || readedLen != 4 {
|
||||
PrintError("获取消息失败。 Error: " + err.Error())
|
||||
return nil
|
||||
}
|
||||
dataLen = binary.BigEndian.Uint32(lenBuffer[0:4])
|
||||
data = make([]byte, dataLen)
|
||||
if dataLen == 0 {
|
||||
return data
|
||||
}
|
||||
readedLen, err = session.conn.Read(data)
|
||||
if err != nil || readedLen != int(dataLen) {
|
||||
PrintError("获取消息失败。Error:" + err.Error())
|
||||
return nil
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
//发送数据
|
||||
func (session *Session) SendData(headInfo map[string]string, data []byte) bool {
|
||||
head, err := json.Marshal(headInfo)
|
||||
if err != nil {
|
||||
PrintError("Marshal Failed.")
|
||||
return false
|
||||
}
|
||||
return session.send(head, data)
|
||||
}
|
||||
|
||||
//发送消息
|
||||
func (session *Session) SendMessage(msg []byte) bool {
|
||||
if session.sender == nil {
|
||||
PrintError("请先登录")
|
||||
return false
|
||||
}
|
||||
if session.receiver == "" {
|
||||
PrintError("请先设定当前聊天好友")
|
||||
return false
|
||||
}
|
||||
if len(msg) == 0 {
|
||||
PrintError("无法发送空消息")
|
||||
return false
|
||||
}
|
||||
PrintMessageHead(session.sender.NickName+" ["+time.Now().Format("2006-01-02 15:04:05")+"]", true)
|
||||
PrintMessage(string(msg))
|
||||
headInfo := MakeHead(operation_send_message)
|
||||
headInfo["To"] = session.receiver
|
||||
return session.SendData(headInfo, msg)
|
||||
}
|
||||
|
||||
//登录
|
||||
func (session *Session) Login(user string, pwd string) bool {
|
||||
logInfo := MakeHead(operation_login)
|
||||
logInfo["User"] = user
|
||||
logInfo["Pwd"] = pwd
|
||||
return session.SendData(logInfo, nil)
|
||||
}
|
||||
|
||||
//请求登出
|
||||
func (session *Session) Logout() {
|
||||
if session.sender == nil {
|
||||
PrintError("当前未登录")
|
||||
return
|
||||
} //未登录
|
||||
headInfo := MakeHead(operation_logout)
|
||||
session.SendData(headInfo, nil)
|
||||
atomic.SwapInt32(&session.status, session_status_created)
|
||||
session.sender = nil
|
||||
session.friends = nil
|
||||
session.receiver = ""
|
||||
SetUserNameText("未登录")
|
||||
SetCurrentFriendText("")
|
||||
PrintLog("已登出.")
|
||||
}
|
||||
|
||||
//获取好友列表
|
||||
func (session *Session) GetUserList() {
|
||||
headInfo := MakeHead(operation_get_user_list)
|
||||
session.SendData(headInfo, nil)
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
//用户信息
|
||||
type UserInfo struct {
|
||||
User string //用户名
|
||||
NickName string //昵称
|
||||
Motto string //签名
|
||||
UGroup string //用户组。ugroup_*常量值
|
||||
}
|
||||
|
||||
//用户组
|
||||
const (
|
||||
ugroup_user string = "User" //普通用户
|
||||
ugroup_vip string = "Vip" //会员
|
||||
ugroup_admin string = "Admin" //管理员
|
||||
ugroup_group string = "Group" //群聊
|
||||
)
|
||||
@@ -0,0 +1,158 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
FOREGROUND_BLUE int = 1
|
||||
FOREGROUND_GREEN int = 2
|
||||
FOREGROUND_RED int = 4
|
||||
FOREGROUND_INTENSITY int = 8
|
||||
BACKGROUND_BLUE int = 16
|
||||
BACKGROUND_GREEN int = 32
|
||||
BACKGROUND_RED int = 64
|
||||
BACKGROUND_INTENSITY int = 128
|
||||
|
||||
FORGROUND_WHITE int = 7
|
||||
)
|
||||
|
||||
var conctrl *syscall.LazyDLL //Windows控制台窗口辅助输出库
|
||||
var consoleWindow uintptr //控制台窗口控制器指针
|
||||
var messagePannel uintptr //消息输出窗格
|
||||
var operationPannel uintptr //操作提示窗格
|
||||
var usrenamePannel uintptr //用户名显示窗格
|
||||
var currentFriendPannel uintptr //当前聊天对象显示窗格
|
||||
var inputPannel uintptr //输入窗格
|
||||
var spliter uintptr //分割线
|
||||
//初始化
|
||||
func InitConsole() {
|
||||
conctrl = syscall.NewLazyDLL("conctrl_x64.dll")
|
||||
proc := conctrl.NewProc("CreateConsoleWindow")
|
||||
consoleWindow, _, _ = proc.Call(100, 999)
|
||||
if consoleWindow == 0 {
|
||||
panic("Failed to init console. conctrl.dll not found.")
|
||||
}
|
||||
proc = conctrl.NewProc("CreatePannel")
|
||||
messagePannel, _, _ = proc.Call(consoleWindow, 0, 0, 100, 20)
|
||||
operationPannel, _, _ = proc.Call(consoleWindow, 0, 21, 20, 1)
|
||||
usrenamePannel, _, _ = proc.Call(consoleWindow, 35, 21, 30, 1)
|
||||
currentFriendPannel, _, _ = proc.Call(consoleWindow, 65, 21, 35, 1)
|
||||
inputPannel, _, _ = proc.Call(consoleWindow, 0, 23, 100, 20)
|
||||
proc = conctrl.NewProc("CreateSpliter")
|
||||
spliter, _, _ = proc.Call(consoleWindow, 0, 22, 100, 0, uintptr(FORGROUND_WHITE))
|
||||
proc = conctrl.NewProc("FocusOnPannel")
|
||||
proc.Call(inputPannel, 0, 0)
|
||||
SetTitle("StormChat")
|
||||
}
|
||||
|
||||
//滚动输出区域
|
||||
func ScrollOutputArea(lineCount int) {
|
||||
var proc *syscall.LazyProc
|
||||
if lineCount > 0 {
|
||||
proc = conctrl.NewProc("ScrollPannelForward")
|
||||
} else {
|
||||
proc = conctrl.NewProc("ScrollPannelBackward")
|
||||
}
|
||||
proc.Call(messagePannel, uintptr(math.Abs(float64(lineCount))))
|
||||
}
|
||||
|
||||
//设置控制台标题
|
||||
func SetTitle(title string) {
|
||||
proc := conctrl.NewProc("SetConsoleWindowTitle")
|
||||
titleB := append([]byte(title), 0)
|
||||
pTtile := *(*uintptr)(unsafe.Pointer(&titleB))
|
||||
proc.Call(uintptr(pTtile), 1)
|
||||
}
|
||||
|
||||
//设置当前操作提示文本
|
||||
func SetOperationText(op string) {
|
||||
proc := conctrl.NewProc("ClearPannel")
|
||||
proc.Call(operationPannel)
|
||||
AddPannelLine(operationPannel, op, false, FORGROUND_WHITE)
|
||||
}
|
||||
|
||||
//设置用户昵称显示区文本
|
||||
func SetUserNameText(name string) {
|
||||
blank := (30 - len(name)) / 2
|
||||
if blank > 0 {
|
||||
name = strings.Repeat(" ", blank) + name
|
||||
}
|
||||
proc := conctrl.NewProc("ClearPannel")
|
||||
proc.Call(usrenamePannel)
|
||||
AddPannelLine(usrenamePannel, name, false, FORGROUND_WHITE)
|
||||
}
|
||||
|
||||
//设置当前聊天好友提示文本
|
||||
func SetCurrentFriendText(friend string) {
|
||||
if friend != "" {
|
||||
friend = "To: " + friend
|
||||
}
|
||||
blank := 34 - len(friend)
|
||||
if blank > 0 {
|
||||
friend = strings.Repeat(" ", blank) + friend
|
||||
}
|
||||
proc := conctrl.NewProc("ClearPannel")
|
||||
proc.Call(currentFriendPannel)
|
||||
AddPannelLine(currentFriendPannel, friend, false, FORGROUND_WHITE)
|
||||
}
|
||||
|
||||
//向输出区写行
|
||||
func PrintOutputLine(text string, attribute int) {
|
||||
AddPannelLine(messagePannel, text, false, attribute)
|
||||
}
|
||||
|
||||
//向输入区写文本
|
||||
func PrintInputTip(text string) {
|
||||
ClearInput()
|
||||
AddPannelText(inputPannel, text, true, FORGROUND_WHITE)
|
||||
}
|
||||
|
||||
//清空输入区
|
||||
func ClearInput() {
|
||||
proc := conctrl.NewProc("ClearPannel")
|
||||
proc.Call(inputPannel)
|
||||
proc = conctrl.NewProc("FocusOnPannel")
|
||||
proc.Call(inputPannel, 0, 0)
|
||||
}
|
||||
|
||||
func ClearOutput() {
|
||||
proc := conctrl.NewProc("ClearPannel")
|
||||
proc.Call(messagePannel)
|
||||
}
|
||||
|
||||
//销毁控制器,恢复控制台
|
||||
func DestroyConsoleWindow() {
|
||||
proc := conctrl.NewProc("FocusOnPannel")
|
||||
proc.Call(messagePannel, 0, 0)
|
||||
proc = conctrl.NewProc("DestroyConsoleWindow")
|
||||
proc.Call(consoleWindow)
|
||||
}
|
||||
|
||||
//向窗格加入文本行
|
||||
func AddPannelLine(pannel uintptr, text string, focus bool, attribute int) {
|
||||
proc := conctrl.NewProc("AddPannelLine")
|
||||
addPannel(proc, pannel, text, focus, attribute)
|
||||
}
|
||||
|
||||
//向窗格加入文本
|
||||
func AddPannelText(pannel uintptr, text string, focus bool, attribute int) {
|
||||
proc := conctrl.NewProc("AddPannelText")
|
||||
addPannel(proc, pannel, text, focus, attribute)
|
||||
}
|
||||
|
||||
//向窗格加入文本(行)
|
||||
func addPannel(proc *syscall.LazyProc, pannel uintptr, text string, focus bool, attribute int) {
|
||||
line := append([]byte(text), 0)
|
||||
pLine := *(*uintptr)(unsafe.Pointer(&line))
|
||||
var focusInt int
|
||||
if focus {
|
||||
focusInt = 1
|
||||
} else {
|
||||
focusInt = 0
|
||||
}
|
||||
proc.Call(pannel, uintptr(pLine), uintptr(focusInt), 1, uintptr(attribute))
|
||||
}
|
||||
Reference in New Issue
Block a user