使用AIGLX的x server, 实现3d是通过在原先2d的server中添加3d指令部分来实现的,3d效果会经过原先2dserver的一个转发, 因此,我们可以利用这一特性,通过让server实现一些原有2d的一些特性间接实现3d效果。就如同我们需要点击gtk的notebook, 让按下其tag时可以伴随有3d虚拟桌面的转换。
刚开始解决这个问题的时候,以为3d部分是将workspace虚拟为各个桌面的结果,结果发现使用这个方法切换桌面时失败率很高,但偶尔可以成功。研究 gnome pager, 发现其每次都可以切成功,而且随着3d虚拟桌面的切换,pager也会随之在各区域内切换。 于是研究了一下gnome pager, 并在我们的代码里加入gnome pager的功能,发现2d下很正常, 3d下竟完全没有效果。 绝望之余仔细研究了一下gnome pager的代码,发现它对3d另有一套viewport的处理机制,于是想起用libxfcegui时也使用了viewport,且每次 viewport改变的值都一样,是不是正是因为这个原因导致3d时每次都不成功呢? 于是决定还是重新回到至少对切换有反应的libxfcegui的实现方案上, 测试了去掉viewport语句,改变不同viewport改变值等方法,确定了下面的方法。
步骤:
1. 发射切换信号
在notebook的switch-page相应回调函数nb_switch_page
g_signal_connect (desktop->priv->notebook, "switch-page", G_CALLBACK (nb_switch_page), NULL);
且在此函数中调用如下代码:
int viewport_x, viewport_y;
viewport_x = netk_screen_get_width (screen) * page_num;
viewport_y = netk_screen_get_height (screen) * page_num;
netk_screen_move_viewport(screen, viewport_x, viewport_y);
其中screen是一个全局静态变量
static NetkScreen *screen;
这里,
netk_screen_move_viewport 是一个libxfcegui4的库函数:
/**
* netk_screen_move_viewport:
* @screen: a #NetkScreen
* @x: X offset of viewport
* @y: Y offset of viewport
*
* Ask window manager to move the viewport of the current workspace.
*/
void netk_screen_move_viewport (NetkScreen * screen, int x, int y)
{
g_return_if_fail (NETK_IS_SCREEN (screen));
g_return_if_fail (x >= 0);
g_return_if_fail (y >= 0);
p_netk_change_viewport (NETK_SCREEN_XSCREEN (screen), x, y);
}
void
p_netk_change_viewport (Screen * screen, int x, int y)
{
XEvent xev;
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.display = gdk_display;
xev.xclient.window = RootWindowOfScreen (screen);
xev.xclient.message_type = p_netk_atom_get ("_NET_DESKTOP_VIEWPORT");
xev.xclient.format = 32;
xev.xclient.data.l[0] = x;
xev.xclient.data.l[1] = y;
xev.xclient.data.l[2] = 0;
xev.xclient.data.l[3] = 0;
XSendEvent (gdk_display,
RootWindowOfScreen (screen),
False,
SubstructureRedirectMask | SubstructureNotifyMask, &xev);
}
可以看到,让notebook不同的page按下时可以跳转到3d桌面的不同面的关键就是要和X server通信,并让其切换当前工作区的viewport(视角)。
2. 接受切换桌面消息
另一方面, 在3D桌面环境下,当切换到 了立方体的另一个面的时候,需要notebook相应也跳动另一个tag,于是让其建立如下信号连接, 让X server在切换了视角之后,通知notebook
g_signal_connect (screen, "viewports_changed", G_CALLBACK (viewports_changed_callback), desktop->priv->notebook);
viewports_changed_callback的实现如下:
static void
viewports_changed_callback (NetkScreen * screen, gpointer data)
{
GtkNotebook *notebook = GTK_NOTEBOOK (data);
NetkWorkspace *space;
space = netk_screen_get_active_workspace(screen);
int viewport_x = netk_workspace_get_viewport_x (space);
int screen_width = netk_screen_get_width (screen);
int page_num = viewport_x / screen_width;
gtk_notebook_set_current_page(notebook, page_num);
}
viewports_changed信号为NetkScreenClass所注册的一个信号:
static void
netk_screen_class_init (NetkScreenClass * klass)
{
...
signals[VIEWPORTS_CHANGED] =
g_signal_new (I_("viewports_changed"),
G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (NetkScreenClass, viewports_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
...
}
附:
Netscreen 及其class的定义:
typedef struct _NetkScreen NetkScreen;
typedef struct _NetkScreenClass NetkScreenClass;
typedef struct _NetkScreenPrivate NetkScreenPrivate;
struct _NetkScreen
{
GObject parent_instance;
NetkScreenPrivate *priv;
};
struct _NetkScreenClass
{
GObjectClass parent_class;
/* focused window changed */
void (*active_window_changed) (NetkScreen * screen);
/* current workspace changed */
void (*active_workspace_changed) (NetkScreen * screen);
/* stacking order changed */
void (*window_stacking_changed) (NetkScreen * screen);
/* window added */
void (*window_opened) (NetkScreen * screen, NetkWindow * window);
/* window removed */
void (*window_closed) (NetkScreen * screen, NetkWindow * window);
/* new workspace */
void (*workspace_created) (NetkScreen * screen, NetkWorkspace * space);
/* workspace gone */
void (*workspace_destroyed) (NetkScreen * screen, NetkWorkspace * space);
/* new app */
void (*application_opened) (NetkScreen * screen, NetkApplication * app);
/* app gone */
void (*application_closed) (NetkScreen * screen, NetkApplication * app);
/* new class group */
void (* class_group_opened) (NetkScreen *screen, NetkClassGroup *class_group);
/* class group gone */
void (* class_group_closed) (NetkScreen *screen, NetkClassGroup *class_group);
/* New background */
void (*background_changed) (NetkScreen * screen);
/* Toggle showing desktop */
void (*showing_desktop_changed) (NetkScreen * screen);
/* Viewport stuff changed */
void (*viewports_changed) (NetkScreen * screen);
void (*pad1) (void);
void (*pad2) (void);
void (*pad3) (void);
void (*pad4) (void);
void (*pad5) (void);
void (*pad6) (void);
void (*pad7) (void);
void (*pad8) (void);
};
struct _NetkScreenPrivate
{
int number;
Window xroot;
Screen *xscreen;
/* in map order */
GList *mapped_windows;
/* in stacking order */
GList *stacked_windows;
/* in 0-to-N order */
GList *workspaces;
NetkWindow *active_window;
NetkWorkspace *active_workspace;
Pixmap bg_pixmap;
guint update_handler;
guint showing_desktop:1;
/* if you add flags, be sure to set them
* when we create the screen so we get an initial update
*/
guint need_update_stack_list:1;
guint need_update_workspace_list:1;
guint need_update_viewport_settings:1;
guint need_update_active_workspace:1;
guint need_update_active_window:1;
guint need_update_workspace_names:1;
guint need_update_bg_pixmap:1;
guint need_update_showing_desktop:1;
};
刚开始解决这个问题的时候,以为3d部分是将workspace虚拟为各个桌面的结果,结果发现使用这个方法切换桌面时失败率很高,但偶尔可以成功。研究 gnome pager, 发现其每次都可以切成功,而且随着3d虚拟桌面的切换,pager也会随之在各区域内切换。 于是研究了一下gnome pager, 并在我们的代码里加入gnome pager的功能,发现2d下很正常, 3d下竟完全没有效果。 绝望之余仔细研究了一下gnome pager的代码,发现它对3d另有一套viewport的处理机制,于是想起用libxfcegui时也使用了viewport,且每次 viewport改变的值都一样,是不是正是因为这个原因导致3d时每次都不成功呢? 于是决定还是重新回到至少对切换有反应的libxfcegui的实现方案上, 测试了去掉viewport语句,改变不同viewport改变值等方法,确定了下面的方法。
步骤:
1. 发射切换信号
在notebook的switch-page相应回调函数nb_switch_page
g_signal_connect (desktop->priv->notebook, "switch-page", G_CALLBACK (nb_switch_page), NULL);
且在此函数中调用如下代码:
int viewport_x, viewport_y;
viewport_x = netk_screen_get_width (screen) * page_num;
viewport_y = netk_screen_get_height (screen) * page_num;
netk_screen_move_viewport(screen, viewport_x, viewport_y);
其中screen是一个全局静态变量
static NetkScreen *screen;
这里,
netk_screen_move_viewport 是一个libxfcegui4的库函数:
/**
* netk_screen_move_viewport:
* @screen: a #NetkScreen
* @x: X offset of viewport
* @y: Y offset of viewport
*
* Ask window manager to move the viewport of the current workspace.
*/
void netk_screen_move_viewport (NetkScreen * screen, int x, int y)
{
g_return_if_fail (NETK_IS_SCREEN (screen));
g_return_if_fail (x >= 0);
g_return_if_fail (y >= 0);
p_netk_change_viewport (NETK_SCREEN_XSCREEN (screen), x, y);
}
void
p_netk_change_viewport (Screen * screen, int x, int y)
{
XEvent xev;
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.display = gdk_display;
xev.xclient.window = RootWindowOfScreen (screen);
xev.xclient.message_type = p_netk_atom_get ("_NET_DESKTOP_VIEWPORT");
xev.xclient.format = 32;
xev.xclient.data.l[0] = x;
xev.xclient.data.l[1] = y;
xev.xclient.data.l[2] = 0;
xev.xclient.data.l[3] = 0;
XSendEvent (gdk_display,
RootWindowOfScreen (screen),
False,
SubstructureRedirectMask | SubstructureNotifyMask, &xev);
}
可以看到,让notebook不同的page按下时可以跳转到3d桌面的不同面的关键就是要和X server通信,并让其切换当前工作区的viewport(视角)。
2. 接受切换桌面消息
另一方面, 在3D桌面环境下,当切换到 了立方体的另一个面的时候,需要notebook相应也跳动另一个tag,于是让其建立如下信号连接, 让X server在切换了视角之后,通知notebook
g_signal_connect (screen, "viewports_changed", G_CALLBACK (viewports_changed_callback), desktop->priv->notebook);
viewports_changed_callback的实现如下:
static void
viewports_changed_callback (NetkScreen * screen, gpointer data)
{
GtkNotebook *notebook = GTK_NOTEBOOK (data);
NetkWorkspace *space;
space = netk_screen_get_active_workspace(screen);
int viewport_x = netk_workspace_get_viewport_x (space);
int screen_width = netk_screen_get_width (screen);
int page_num = viewport_x / screen_width;
gtk_notebook_set_current_page(notebook, page_num);
}
viewports_changed信号为NetkScreenClass所注册的一个信号:
static void
netk_screen_class_init (NetkScreenClass * klass)
{
...
signals[VIEWPORTS_CHANGED] =
g_signal_new (I_("viewports_changed"),
G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (NetkScreenClass, viewports_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
...
}
附:
Netscreen 及其class的定义:
typedef struct _NetkScreen NetkScreen;
typedef struct _NetkScreenClass NetkScreenClass;
typedef struct _NetkScreenPrivate NetkScreenPrivate;
struct _NetkScreen
{
GObject parent_instance;
NetkScreenPrivate *priv;
};
struct _NetkScreenClass
{
GObjectClass parent_class;
/* focused window changed */
void (*active_window_changed) (NetkScreen * screen);
/* current workspace changed */
void (*active_workspace_changed) (NetkScreen * screen);
/* stacking order changed */
void (*window_stacking_changed) (NetkScreen * screen);
/* window added */
void (*window_opened) (NetkScreen * screen, NetkWindow * window);
/* window removed */
void (*window_closed) (NetkScreen * screen, NetkWindow * window);
/* new workspace */
void (*workspace_created) (NetkScreen * screen, NetkWorkspace * space);
/* workspace gone */
void (*workspace_destroyed) (NetkScreen * screen, NetkWorkspace * space);
/* new app */
void (*application_opened) (NetkScreen * screen, NetkApplication * app);
/* app gone */
void (*application_closed) (NetkScreen * screen, NetkApplication * app);
/* new class group */
void (* class_group_opened) (NetkScreen *screen, NetkClassGroup *class_group);
/* class group gone */
void (* class_group_closed) (NetkScreen *screen, NetkClassGroup *class_group);
/* New background */
void (*background_changed) (NetkScreen * screen);
/* Toggle showing desktop */
void (*showing_desktop_changed) (NetkScreen * screen);
/* Viewport stuff changed */
void (*viewports_changed) (NetkScreen * screen);
void (*pad1) (void);
void (*pad2) (void);
void (*pad3) (void);
void (*pad4) (void);
void (*pad5) (void);
void (*pad6) (void);
void (*pad7) (void);
void (*pad8) (void);
};
struct _NetkScreenPrivate
{
int number;
Window xroot;
Screen *xscreen;
/* in map order */
GList *mapped_windows;
/* in stacking order */
GList *stacked_windows;
/* in 0-to-N order */
GList *workspaces;
NetkWindow *active_window;
NetkWorkspace *active_workspace;
Pixmap bg_pixmap;
guint update_handler;
guint showing_desktop:1;
/* if you add flags, be sure to set them
* when we create the screen so we get an initial update
*/
guint need_update_stack_list:1;
guint need_update_workspace_list:1;
guint need_update_viewport_settings:1;
guint need_update_active_workspace:1;
guint need_update_active_window:1;
guint need_update_workspace_names:1;
guint need_update_bg_pixmap:1;
guint need_update_showing_desktop:1;
};